123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- #include "main.h"
- #include <windows.h>
- #include "api__in_flv.h"
- #include "../Winamp/wa_ipc.h"
- #include "FLVHeader.h"
- #include "FLVStreamHeader.h"
- #include "FLVAudioHeader.h"
- #include "FLVVideoHeader.h"
- #include "FLVMetadata.h"
- #include <malloc.h>
- #include <stdio.h>
- #include <shlwapi.h>
- #include "VideoThread.h"
- #include "FLVReader.h"
- #include "resource.h"
- #include "FLVCOM.h"
- #include <api/service/waservicefactory.h>
- #include "ifc_flvaudiodecoder.h"
- #include "svc_flvdecoder.h"
- #include "../nu/AudioOutput.h"
- #define PRE_BUFFER_MS 5000.0
- #define PRE_BUFFER_MAX (1024*1024)
- uint32_t last_timestamp=0;
- static bool audioOpened;
- int bufferCount;
- static int bits, channels, sampleRate;
- static double dataRate_audio, dataRate_video;
- static double dataRate;
- static uint64_t prebuffer;
- bool mute=false;
- uint32_t first_timestamp;
- static size_t audio_buffered=0;
- void VideoStop();
- static ifc_flvaudiodecoder *audioDecoder=0;
- static bool checked_in_swf=false;
- extern bool video_only;
- class FLVWait
- {
- public:
- int WaitOrAbort(int len)
- {
- if (WaitForSingleObject(killswitch, len) == WAIT_OBJECT_0)
- return 1;
- return 0;
- }
- };
- static nu::AudioOutput<FLVWait> outputter(&plugin);
- static void Buffering(int bufStatus, const wchar_t *displayString)
- {
- if (bufStatus < 0 || bufStatus > 100)
- return;
- char tempdata[75*2] = {0, };
- int csa = plugin.SAGetMode();
- if (csa & 1)
- {
- for (int x = 0; x < bufStatus*75 / 100; x ++)
- tempdata[x] = x * 16 / 75;
- }
- else if (csa&2)
- {
- int offs = (csa & 1) ? 75 : 0;
- int x = 0;
- while (x < bufStatus*75 / 100)
- {
- tempdata[offs + x++] = -6 + x * 14 / 75;
- }
- while (x < 75)
- {
- tempdata[offs + x++] = 0;
- }
- }
- else if (csa == 4)
- {
- tempdata[0] = tempdata[1] = (bufStatus * 127 / 100);
- }
- if (csa) plugin.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa);
- /*
- TODO
- wchar_t temp[64] = {0};
- StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus);
- SetStatus(temp);
- */
- //SetVideoStatusText(temp); // TODO: find a way to set the old status back
- videoOutput->notifyBufferState(static_cast<int>(bufStatus*2.55f));
- }
- static bool Audio_IsSupported(int type)
- {
- size_t n = 0;
- waServiceFactory *factory = NULL;
- while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
- {
- svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
- if (creator)
- {
- int supported = creator->HandlesAudio(type);
- factory->releaseInterface(creator);
- if (supported == svc_flvdecoder::CREATEDECODER_SUCCESS)
- return true;
- }
- }
- return false;
- }
- static ifc_flvaudiodecoder *CreateAudioDecoder(const FLVAudioHeader &header)
- {
- ifc_flvaudiodecoder *audio_decoder=0;
- size_t n=0;
- waServiceFactory *factory = NULL;
- while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
- {
- svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
- if (creator)
- {
- if (creator->CreateAudioDecoder(header.stereo, header.bits, header.sampleRate, header.format, &audio_decoder) == FLV_AUDIO_SUCCESS)
- return audio_decoder;
- factory->releaseInterface(creator);
- }
- }
- return 0;
- }
- void OnStart()
- {
- Video_Init();
- audioOpened = false;
- audioDecoder = 0;
- bufferCount=0;
- mute = false;
- audio_buffered=0;
- if (!videoOutput)
- videoOutput = (IVideoOutput *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
- }
- void Audio_Close()
- {
- if (audioDecoder)
- audioDecoder->Close();
- audioDecoder=0;
- }
- static bool OpenAudio(unsigned int sampleRate, unsigned int channels, unsigned int bits)
- {
- if (!outputter.Open(0, channels, sampleRate, bits, -666))
- return false;
- audioOpened = true;
- return true;
- }
- static void DoBuffer(FLVReader &reader)
- {
- // TODO: we should pre-buffer after getting the first audio + video frames
- // so we can estimate bitrate
- for (;;)
- {
- uint64_t processedPosition = reader.GetProcessedPosition();
- if (processedPosition < prebuffer && !reader.IsEOF())
- {
- Buffering((int)((100ULL * processedPosition) / prebuffer), WASABI_API_LNGSTRINGW(IDS_BUFFERING));
- if (WaitForSingleObject(killswitch, 100) == WAIT_OBJECT_0)
- break;
- else
- continue;
- }
- else
- break;
- }
- }
- char pcmdata[65536] = {0};
- size_t outlen = 32768;
- static uint32_t audio_type;
- static void OnAudio(FLVReader &reader, void *data, size_t length, const FLVAudioHeader &header)
- {
- if (!audioDecoder)
- {
- audioDecoder = CreateAudioDecoder(header);
- audio_type = header.format;
- video_only=false;
- }
- if (audioDecoder)
- {
- if (!audioDecoder->Ready())
- {
- //first_timestamp = -1;
- }
- outlen = sizeof(pcmdata)/2 - audio_buffered;
- double bitrate = 0;
- int ret = audioDecoder->DecodeSample(data, length, pcmdata+audio_buffered, &outlen, &bitrate);
- if (ret == FLV_AUDIO_SUCCESS)
- {
- outlen+=audio_buffered;
- audio_buffered=0;
- if (bitrate && dataRate_audio != bitrate)
- {
- dataRate_audio = bitrate;
- dataRate = dataRate_audio + dataRate_video;
- plugin.SetInfo((int)dataRate, -1, -1, 1);
- }
- if (!audioOpened)
- {
- // pre-populate values for decoders that use the header info (e.g. ADPCM)
- sampleRate=header.sampleRate;
- channels = header.stereo?2:1;
- bits = header.bits;
- if (audioDecoder->GetOutputFormat((unsigned int *)&sampleRate, (unsigned int *)&channels, (unsigned int *)&bits) == FLV_AUDIO_SUCCESS)
- {
- // buffer (if needed)
- prebuffer = (uint64_t)(dataRate * PRE_BUFFER_MS / 8.0);
- if (prebuffer > PRE_BUFFER_MAX)
- prebuffer=PRE_BUFFER_MAX;
- DoBuffer(reader); // benski> admittedly a crappy place to call this
- if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
- return;
- OpenAudio(sampleRate, channels, bits);
- }
- }
- if (mute)
- {
- if (bits == 8) // 8 bit is signed so 128 is zero voltage
- memset(pcmdata, 0x80, outlen);
- else
- memset(pcmdata, 0, outlen);
- }
- if (audioOpened && outlen)
- {
- outputter.Write(pcmdata, outlen);
- }
- else
- {
- audio_buffered=outlen;
- }
- }
- else if (ret == FLV_AUDIO_NEEDS_MORE_INPUT)
- {
- plugin.SetInfo(-1, -1, -1, 0);
- }
- }
- }
- #define PREBUFFER_BYTES 2048ULL
- enum
- {
- CODEC_CHECK_NONE=0,
- CODEC_CHECK_AUDIO=1,
- CODEC_CHECK_VIDEO=2,
- CODEC_CHECK_UNSURE = -1,
- };
- static bool CheckSWF()
- {
- if (!checked_in_swf)
- {
- const wchar_t *pluginsDir = (const wchar_t *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW);
- wchar_t in_swf_path[MAX_PATH] = {0};
- PathCombineW(in_swf_path, pluginsDir, L"in_swf.dll");
- in_swf = LoadLibraryW(in_swf_path);
- checked_in_swf = true;
- }
- return !!in_swf;
- }
- static void CALLBACK SWFAPC(ULONG_PTR param)
- {
- if (in_swf)
- {
- typedef In_Module *(*MODULEGETTER)();
- MODULEGETTER moduleGetter=0;
- moduleGetter = (MODULEGETTER)GetProcAddress(in_swf, "winampGetInModule2");
- if (moduleGetter)
- swf_mod = moduleGetter();
- }
- if (swf_mod)
- {
- if (swf_mod->Play(playFile))
- swf_mod=0;
- }
- if (!swf_mod)
- {
- if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- }
- }
- static bool Audio_DecoderReady()
- {
- if (!audioDecoder)
- return true;
- return !!audioDecoder->Ready();
- }
- static bool DecodersReady()
- {
- return Audio_DecoderReady() && Video_DecoderReady();
- }
- DWORD CALLBACK PlayProcedure(LPVOID param)
- {
- int needCodecCheck=CODEC_CHECK_UNSURE;
- int missingCodecs=CODEC_CHECK_NONE;
- size_t codecCheckFrame=0;
- dataRate_audio=0;
- dataRate_video=0;
- dataRate=0;
- first_timestamp=-1;
- outputter.Init(plugin.outMod);
- FLVReader reader(playFile);
- size_t i=0;
- bool hasDuration=false;
- OnStart();
- plugin.is_seekable=0;
- prebuffer = PREBUFFER_BYTES;
- bool first_frame_parsed=false;
- for (;;)
- {
- DoBuffer(reader);
- if (WaitForSingleObject(killswitch, paused?100:0) == WAIT_OBJECT_0)
- break;
- if (paused)
- continue;
- if (m_need_seek != -1 && DecodersReady() && first_frame_parsed)
- {
- if (reader.GetPosition(m_need_seek, &i, video_opened))
- {
- VideoFlush();
- if (audioDecoder)
- audioDecoder->Flush();
- FrameData frameData;
- reader.GetFrame(i, frameData);
- outputter.Flush(frameData.header.timestamp);
- if (video_only)
- {
- video_clock.Seek(frameData.header.timestamp);
- }
- }
- uint32_t first_timestamp = 0;
- m_need_seek=-1;
- }
- // update the movie length
- if (!hasDuration)
- {
- if (reader.IsStreaming())
- {
- hasDuration=true;
- g_length = -1000;
- plugin.is_seekable=0;
- }
- else
- {
- g_length=reader.GetMaxTimestamp();
- if (g_length != -1000)
- plugin.is_seekable=1;
- }
- PostMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
- }
- /** if it's a local file or an HTTP asset with content-length
- ** we verify that the FLV has codecs we can decode
- ** depending on settings, we will do one of the following with unsupport assets
- ** 1) Play anyway (e.g. an H.264 video might play audio only)
- ** 2) Use in_swf to play back
- ** 3) skip
- **/
- if (needCodecCheck == CODEC_CHECK_UNSURE)
- {
- FLVHeader *header = reader.GetHeader();
- if (header)
- {
- needCodecCheck=CODEC_CHECK_NONE;
- if (!reader.IsStreaming())
- {
- if (header->hasVideo)
- needCodecCheck |= CODEC_CHECK_VIDEO;
- if (header->hasAudio)
- needCodecCheck |= CODEC_CHECK_AUDIO;
- }
- if (!header->hasAudio)
- {
- video_only=true;
- video_clock.Start();
- }
- }
- }
- if (needCodecCheck)
- {
- FrameData frameData;
- if (reader.GetFrame(codecCheckFrame, frameData))
- {
- FLVStreamHeader &frameHeader = frameData.header;
- if ((needCodecCheck & CODEC_CHECK_AUDIO) && frameHeader.type == FLV::FRAME_TYPE_AUDIO)
- {
- reader.Seek(frameData.location+15); // TODO: check for -1 return value
- uint8_t data[1] = {0};
- FLVAudioHeader audioHeader;
- size_t bytesRead = reader.Read(data, 1);
- if (audioHeader.Read(data, bytesRead))
- {
- if (Audio_IsSupported(audioHeader.format))
- {
- needCodecCheck &= ~CODEC_CHECK_AUDIO;
- }
- else
- {
- needCodecCheck &= ~CODEC_CHECK_AUDIO;
- missingCodecs|=CODEC_CHECK_AUDIO;
- video_only=true;
- }
- }
- }
- if ((needCodecCheck & CODEC_CHECK_VIDEO) && frameHeader.type == FLV::FRAME_TYPE_VIDEO)
- {
- reader.Seek(frameData.location+15); // TODO: check for -1 return value
- uint8_t data[1] = {0};
- FLVVideoHeader videoHeader;
- size_t bytesRead = reader.Read(data, 1);
- if (videoHeader.Read(data, bytesRead))
- {
- if (Video_IsSupported(videoHeader.format))
- {
- needCodecCheck &= ~CODEC_CHECK_VIDEO;
- }
- else
- {
- needCodecCheck &= ~CODEC_CHECK_VIDEO;
- missingCodecs|=CODEC_CHECK_VIDEO;
- }
- }
- }
- codecCheckFrame++;
- }
- else if (reader.IsEOF())
- break;
- else if (WaitForSingleObject(killswitch, 55) == WAIT_OBJECT_0)
- break;
- }
- if (needCodecCheck)
- continue; // don't start decoding until we've done our codec check
- if (missingCodecs)
- {
- // use in_swf to play this one
- if (CheckSWF())
- {
- HANDLE mainThread = WASABI_API_APP->main_getMainThreadHandle();
- if (mainThread)
- {
- reader.Kill();
- Audio_Close();
- Video_Stop();
- Video_Close();
- QueueUserAPC(SWFAPC, mainThread,0);
- CloseHandle(mainThread);
- return 0;
- }
- }
- else
- {
- FLVHeader *header = reader.GetHeader();
- if (header)
- {
- bool can_play_something = false;
- if (header->hasVideo && !(missingCodecs & CODEC_CHECK_VIDEO))
- can_play_something = true; // we can play video
- else if (header->hasAudio && !(missingCodecs & CODEC_CHECK_AUDIO))
- can_play_something = true; // we can play audio
- if (can_play_something)
- {
- missingCodecs=false;
- continue;
- }
- }
- break; // no header or no codecs at all, bail out
- }
- }
- /* --- End Codec Check --- */
- FrameData frameData;
- if (reader.GetFrame(i, frameData))
- {
- i++;
- uint8_t data[2] = {0};
- FLVStreamHeader &frameHeader = frameData.header;
- reader.Seek(frameData.location+15); // TODO: check for -1 return value
- switch (frameHeader.type)
- {
- default:
- #ifdef _DEBUG
- DebugBreak();
- #endif
- break;
- case FLV::FRAME_TYPE_AUDIO: // audio
- first_frame_parsed=true;
- if (m_need_seek == -1 || !Audio_DecoderReady())
- {
- FLVAudioHeader audioHeader;
- size_t bytesRead = reader.Read(data, 1);
- if (audioHeader.Read(data, bytesRead))
- {
- size_t dataSize = frameHeader.dataSize - 1;
- uint8_t *audiodata = (uint8_t *)calloc(dataSize, sizeof(uint8_t));
- if (audiodata)
- {
- bytesRead = reader.Read(audiodata, dataSize);
- if (bytesRead != dataSize)
- break;
- if (!reader.IsStreaming())
- {
- if (first_timestamp == -1)
- first_timestamp = frameHeader.timestamp;
- last_timestamp = frameHeader.timestamp;
- last_timestamp = plugin.outMod->GetWrittenTime();
- }
- OnAudio(reader, audiodata, dataSize, audioHeader);
- free(audiodata);
- }
- }
- }
- break;
- case FLV::FRAME_TYPE_VIDEO: // video
- first_frame_parsed=true;
- if (m_need_seek == -1 || !Video_DecoderReady())
- {
- FLVVideoHeader videoHeader;
- size_t bytesRead = reader.Read(data, 1);
- if (videoHeader.Read(data, bytesRead))
- {
- size_t dataSize = frameHeader.dataSize - 1;
- uint8_t *videodata = (uint8_t *)calloc(dataSize, sizeof(uint8_t));
- if (videodata)
- {
- bytesRead = reader.Read(videodata, dataSize);
- if (bytesRead != dataSize)
- {
- free(videodata);
- break;
- }
- if (!OnVideo(videodata, dataSize, videoHeader.format, frameHeader.timestamp))
- free(videodata);
- }
- }
- }
- break;
- case FLV::FRAME_TYPE_METADATA: // metadata
- {
- first_frame_parsed=true;
- size_t dataSize = frameHeader.dataSize;
- uint8_t *metadatadata= (uint8_t *)calloc(dataSize, sizeof(uint8_t));
- if (metadatadata)
- {
- size_t bytesRead = reader.Read(metadatadata, dataSize);
- if (bytesRead != dataSize)
- {
- free(metadatadata);
- break;
- }
- FLVMetadata metadata;
- metadata.Read(metadatadata, dataSize);
- for ( FLVMetadata::Tag *tag : metadata.tags )
- {
- if (!_wcsicmp(tag->name.str, L"onMetaData"))
- {
- AMFType *amf_stream_title;
- amf_stream_title = tag->parameters->array[L"streamTitle"];
- if (amf_stream_title && amf_stream_title->type == AMFType::TYPE_STRING)
- {
- AMFString *stream_title_string = (AMFString *)amf_stream_title;
- Nullsoft::Utility::AutoLock stream_lock(stream_title_guard);
- free(stream_title);
- stream_title = _wcsdup(stream_title_string->str);
- PostMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
- }
- AMFType *w, *h;
- w = tag->parameters->array[L"width"];
- h = tag->parameters->array[L"height"];
- if (w && h)
- {
- width = (int)AMFGetDouble(w);
- height = (int)AMFGetDouble(h);
- }
- AMFType *duration;
- duration=tag->parameters->array[L"duration"];
- if (duration)
- {
- hasDuration=true;
- plugin.is_seekable=1;
- g_length = (int)(AMFGetDouble(duration)*1000.0);
- }
- // grab the data rate. we'll need this to determine a good pre-buffer.
- AMFType *videoDataRate, *audioDataRate;
- videoDataRate=tag->parameters->array[L"videodatarate"];
- audioDataRate=tag->parameters->array[L"audiodatarate"];
- if (videoDataRate || audioDataRate)
- {
- dataRate_audio = audioDataRate?AMFGetDouble(audioDataRate):0.0;
- dataRate_video = videoDataRate?AMFGetDouble(videoDataRate):0.0;
- dataRate = dataRate_audio + dataRate_video;
- if (dataRate < 1.0f)
- dataRate = 720.0f;
- plugin.SetInfo((int)dataRate, -1, -1, 1);
- prebuffer = (uint64_t)(dataRate * PRE_BUFFER_MS / 8.0);
- if (prebuffer > PRE_BUFFER_MAX)
- prebuffer=PRE_BUFFER_MAX;
- PostMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
- }
- }
- flvCOM.MetadataCallback(tag);
- }
- free(metadatadata);
- }
- }
- break;
- }
- }
- else if (reader.IsEOF())
- break;
- else if (WaitForSingleObject(killswitch, 55) == WAIT_OBJECT_0)
- break;
- }
- reader.SignalKill();
- if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
- {
- outputter.Write(0,0);
- outputter.WaitWhilePlaying();
-
- if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- }
- SetEvent(killswitch);
- Video_Stop();
- Video_Close();
- reader.Kill();
- Audio_Close();
- return 0;
- }
|