📄 chxclientsink.cpp
字号:
/* ***** BEGIN LICENSE BLOCK ***** * Source last modified: $Id: CHXClientSink.cpp,v 1.18.2.4 2004/07/09 01:49:47 hubbe Exp $ * * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. * * The contents of this file, and the files included with this file, * are subject to the current version of the RealNetworks Public * Source License (the "RPSL") available at * http://www.helixcommunity.org/content/rpsl unless you have licensed * the file under the current version of the RealNetworks Community * Source License (the "RCSL") available at * http://www.helixcommunity.org/content/rcsl, in which case the RCSL * will apply. You may also obtain the license terms directly from * RealNetworks. You may not use this file except in compliance with * the RPSL or, if you have a valid RCSL with RealNetworks applicable * to this file, the RCSL. Please see the applicable RPSL or RCSL for * the rights, obligations and limitations governing use of the * contents of the file. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL") in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your version of * this file only under the terms of the GPL, and not to allow others * to use your version of this file under the terms of either the RPSL * or RCSL, indicate your decision by deleting the provisions above * and replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient may * use your version of this file under the terms of any one of the * RPSL, the RCSL or the GPL. * * This file is part of the Helix DNA Technology. RealNetworks is the * developer of the Original Code and owns the copyrights in the * portions it created. * * This file, and the files included with this file, is distributed * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET * ENJOYMENT OR NON-INFRINGEMENT. * * Technology Compatibility Kit Test Suite(s) Location: * http://www.helixcommunity.org/content/tck * * Contributor(s): * * ***** END LICENSE BLOCK ***** */#include "CHXClientSink.h"#include "CHXClientDebug.h"#include "enter_hx_headers.h"#include "hxerror.h"#include "hxcore.h"#include "ihxpckts.h"#include "hxsmartptr.h"HX_SMART_POINTER_INLINE( SPIHXBuffer, IHXBuffer );HX_SMART_POINTER_INLINE( SPIHXValues, IHXValues );HX_SMART_POINTER_INLINE( SPIHXPersistentComponentManager, IHXPersistentComponentManager );HX_SMART_POINTER_INLINE( SPIHXPersistentComponent, IHXPersistentComponent );HX_SMART_POINTER_INLINE( SPIHXAudioPlayer, IHXAudioPlayer );HX_SMART_POINTER_INLINE( SPIHXErrorSinkControl, IHXErrorSinkControl );HX_SMART_POINTER_INLINE( SPIHXErrorMessages, IHXErrorMessages );HX_SMART_POINTER_INLINE( SPIHXScheduler, IHXScheduler );#ifdef HELIX_FEATURE_REGISTRY#include "hxcomm.h" // IHXRegistryID#include "hxmon.h" // IHXRegistryHX_SMART_POINTER_INLINE( SPIHXRegistry, IHXRegistry );HX_SMART_POINTER_INLINE( SPIHXRegistryID, IHXRegistryID );#endif#include "exit_hx_headers.h"#include "HXClientConstants.h"#include "hlxclib/stdlib.h"#include "hlxclib/string.h"UINT32 const kInvalidSeekPosition = 0xFFFFFFFF;static const char* const kURLPropertyName = "url";static const char* const kSourcePropertyName = "src";static const char* const kPersistentComponentIDPropertyName = "PersistentComponentID";#pragma mark -static intUnhex( char c ){ return (c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10 );}static voidUnescapeURL( char* s ){/* * Remove URL hex escapes from s... done in place. The basic concept for * this routine is borrowed from the WWW library HTUnEscape() routine. */ char* p = NULL; bool bProcessingOptionsPastQuestionMark = false; for ( p = s; *s != '\0'; ++s ) { if ( ( !bProcessingOptionsPastQuestionMark ) && ( *s == '%' ) ) { if ( *++s != '\0' ) { *p = Unhex( *s ) << 4; } if ( *++s != '\0' ) { *p++ += Unhex( *s ); } } else { if ( *s == '?' ) { bProcessingOptionsPastQuestionMark = true; } *p++ = *s; } } *p = '\0';}CHXClientSink::~CHXClientSink( void ){ delete [] m_pRPURLTarget; delete [] m_pRPURL; delete [] m_pContextURL; delete [] m_pTrackURL; delete [] m_pMetafileURL; delete [] m_pTitle;}CHXClientSink::CHXClientSink( IHXPlayer* pIHXPlayer, void* userInfo, const HXClientCallbacks* pClientCallbacks ) : m_UserInfo( userInfo ) , m_pClientCallbacks( pClientCallbacks ) , m_pIHXCorePlayer( pIHXPlayer ) , m_hScheduler( 0 )#ifdef HELIX_FEATURE_REGISTRY , m_TitlePropID( 0 ) , m_ClipBandwidthPropID( 0 )#endif , m_TitleSize( 0 ) , m_pTitle( NULL ) , m_pMetafileURL( NULL ) , m_pTrackURL( NULL ) , m_pContextURL( NULL ) , m_pRPURL( NULL ) , m_pRPURLTarget( NULL ) , m_ClipBandwidth( 0 ) , m_ContentState( kContentStateStopped ) // If this object is loaded, kContentStateNotLoaded is not applicable , m_BufferPercent( 100 ) , m_Position( 0 ) , m_Length( 0 ) , m_OnPlayingPositionThreshold( 0 ) , m_PendingBeginPosition( kInvalidSeekPosition ) , m_IsLive( false ) , m_IsGroupsListDirty( false ) , m_HasContentBegun( false ){ CHXASSERT( m_pClientCallbacks ); // What's the point in observing if we cannot tell anyone about it. CHXASSERT( m_pIHXCorePlayer ); // Need a valid core player.}BEGIN_INTERFACE_LIST_NOCREATE( CHXClientSink ) INTERFACE_LIST_ENTRY_SIMPLE( IHXClientAdviseSink ) INTERFACE_LIST_ENTRY_SIMPLE( IHXGroupSink ) INTERFACE_LIST_ENTRY_SIMPLE( IHXVolumeAdviseSink )#ifdef HELIX_FEATURE_REGISTRY INTERFACE_LIST_ENTRY_SIMPLE( IHXPropWatchResponse )#endif INTERFACE_LIST_ENTRY_SIMPLE( IHXErrorSink ) INTERFACE_LIST_ENTRY_SIMPLE( IHXCallback )END_INTERFACE_LISTvoidCHXClientSink::Init( void ){ SetUpPropWatcher(); SPIHXAudioPlayer spAudioPlayer = m_pIHXCorePlayer; if ( spAudioPlayer.IsValid() ) { IHXVolume* pIClientVolume = spAudioPlayer->GetDeviceVolume(); if ( pIClientVolume ) { pIClientVolume->AddAdviseSink( this ); pIClientVolume->Release(); } } SPIHXErrorSinkControl spErrorSinkControl = m_pIHXCorePlayer; if ( spErrorSinkControl.IsValid() ) { spErrorSinkControl->AddErrorSink( this, HXLOG_EMERG, HXLOG_INFO ); }}voidCHXClientSink::Destroy( void ){ if ( m_hScheduler != 0 ) { SPIHXScheduler spScheduler = m_pIHXCorePlayer; if ( spScheduler.IsValid() ) { m_hScheduler = spScheduler->Remove( m_hScheduler ); } } SPIHXErrorSinkControl spErrorSinkControl = m_pIHXCorePlayer; if ( spErrorSinkControl.IsValid() ) { spErrorSinkControl->RemoveErrorSink( this ); } SPIHXAudioPlayer spAudioPlayer = m_pIHXCorePlayer; if ( spAudioPlayer.IsValid() ) { IHXVolume* pIClientVolume = spAudioPlayer->GetDeviceVolume(); if ( pIClientVolume ) { pIClientVolume->RemoveAdviseSink( this ); pIClientVolume->Release(); } } DestroyPropWatcher();}#pragma mark -voidCHXClientSink::SetBeginPosition( UINT32 beginPosition ){ if ( m_ContentState == kContentStateStopped ) { // XXXSEH: This has issues with various pieces of content including http clips and complex clips whose timeline is not known at the beginning. // m_PendingBeginPosition = beginPosition; // Need to wait for an OnBegin() callback before we can change the clip's position. } else { m_PendingBeginPosition = kInvalidSeekPosition; ( void ) m_pIHXCorePlayer->Seek( beginPosition ); }}voidCHXClientSink::DoGroupsListUpdate( void ){ // we defer calling OnGroupsChanged until a playback transition, since at the times // the group changes really occur, the information about the track names isn't yet // available if ( m_IsGroupsListDirty ) { m_IsGroupsListDirty = false; if ( m_pClientCallbacks->OnGroupsChanged ) { m_pClientCallbacks->OnGroupsChanged( m_UserInfo ); } }}voidCHXClientSink::UpdateContentState( int newContentState ){ if ( m_ContentState != newContentState ) { if ( m_ContentState == kContentStateContacting ) { if ( m_pClientCallbacks->OnContacting ) { m_pClientCallbacks->OnContacting( m_UserInfo, NULL ); } } int oldContentState = m_ContentState; m_ContentState = newContentState; if ( m_pClientCallbacks->OnContentStateChanged ) { m_pClientCallbacks->OnContentStateChanged( m_UserInfo, oldContentState, m_ContentState ); } }}STDMETHODIMPCHXClientSink::OnPresentationOpened( void ){ return HXR_OK;}STDMETHODIMPCHXClientSink::OnBegin( ULONG32 ulTime ){ m_HasContentBegun = true; m_Position = ulTime; m_OnPlayingPositionThreshold = ulTime; DoGroupsListUpdate(); // updates group information for second and later clip if ( m_PendingBeginPosition != kInvalidSeekPosition ) { UINT32 beginPosition = m_PendingBeginPosition; m_PendingBeginPosition = kInvalidSeekPosition; ( void ) m_pIHXCorePlayer->Seek( beginPosition ); } return HXR_OK;}STDMETHODIMPCHXClientSink::OnContacting( const char* pHostName ){ UpdateContentState( kContentStateContacting ); if ( m_pClientCallbacks->OnContacting ) { m_pClientCallbacks->OnContacting( m_UserInfo, pHostName ); } return HXR_OK;}STDMETHODIMPCHXClientSink::OnBuffering( ULONG32 ulFlags, UINT16 unPercentComplete ){ if ( m_BufferPercent != unPercentComplete ) { m_OnPlayingPositionThreshold = m_pIHXCorePlayer->GetCurrentPlayTime(); m_BufferPercent = unPercentComplete; if ( m_BufferPercent < 100 ) { DoGroupsListUpdate(); // update information on first clip // I've noticed that this callback is being made even when the content is paused, // so let's prevent the content state from changing in this case. if ( m_HasContentBegun ) { UpdateContentState( kContentStateLoading ); } } if ( m_pClientCallbacks->OnBuffering ) { m_pClientCallbacks->OnBuffering( m_UserInfo, ulFlags, m_BufferPercent ); } } return HXR_OK;}STDMETHODIMPCHXClientSink::OnPosLength( UINT32 ulPosition, UINT32 ulLength ){ bool wasLive = m_IsLive; m_Position = ulPosition; m_IsLive = ( 0 != m_pIHXCorePlayer->IsLive() ); if ( ( m_Length != ulLength ) || ( !wasLive != !m_IsLive ) ) { m_Length = ulLength; if ( m_pClientCallbacks->OnLengthChanged ) { m_pClientCallbacks->OnLengthChanged( m_UserInfo, m_Length ); } } if ( m_Position > m_OnPlayingPositionThreshold ) { DoGroupsListUpdate(); // update information on first clip m_OnPlayingPositionThreshold = 0xFFFFFFFF; UpdateContentState( kContentStatePlaying ); } return HXR_OK;}STDMETHODIMPCHXClientSink::OnPause( ULONG32 ulTime ){ m_HasContentBegun = false; m_Position = ulTime; m_OnPlayingPositionThreshold = ulTime; UpdateContentState( kContentStatePaused ); return HXR_OK;} STDMETHODIMPCHXClientSink::OnStop( void ){ m_HasContentBegun = false; m_Position = 0; // XXXSEH: Might be nice to retain the last position. m_OnPlayingPositionThreshold = 0; return HXR_OK;}STDMETHODIMPCHXClientSink::OnPresentationClosed( void ){ if ( m_ContentState != kContentStateStopped ) { // I'd prefer to send this out in OnStop(), but unfortunately it and this callback are called // prior to Helix being ready to immediately accept a new url for playback. // This kludge solves this problem, providing support for custom playlist managers for example. if ( m_hScheduler == 0 ) { SPIHXScheduler spScheduler = m_pIHXCorePlayer; if ( spScheduler.IsValid() ) { m_hScheduler = spScheduler->RelativeEnter( this, 0 ); } // Couldn't schedule the callback to update the state, so let's do it now. // Not ideal, but better than never altering the state to stopped. if ( m_hScheduler == 0 ) { UpdateContentState( kContentStateStopped ); } } } return HXR_OK;}voidCHXClientSink::ProcessPendingStateChange( void ){ if ( m_hScheduler != 0 ) { SPIHXScheduler spScheduler = m_pIHXCorePlayer; if ( spScheduler.IsValid() ) { m_hScheduler = spScheduler->Remove( m_hScheduler ); } UpdateContentState( kContentStateStopped ); }}STDMETHODIMPCHXClientSink::Func( void ){ m_hScheduler = 0; UpdateContentState( kContentStateStopped ); if ( m_pClientCallbacks->OnContentConcluded ) { m_pClientCallbacks->OnContentConcluded( m_UserInfo ); } return HXR_OK;}STDMETHODIMPCHXClientSink::OnStatisticsChanged( void ){ return HXR_OK;}STDMETHODIMPCHXClientSink::OnPreSeek( ULONG32 ulOldTime, ULONG32 ulNewTime ){ m_Position = ulNewTime; return HXR_OK;}STDMETHODIMPCHXClientSink::OnPostSeek( ULONG32 ulOldTime, ULONG32 ulNewTime ){ m_Position = ulNewTime; m_OnPlayingPositionThreshold = ulNewTime; return HXR_OK;}#pragma mark -voidCHXClientSink::GetURLsFromTrackProperties( IHXValues* pTrackProperties ){ delete [] m_pMetafileURL; m_pMetafileURL = NULL; delete [] m_pTrackURL; m_pTrackURL = NULL; if ( pTrackProperties ) { SPIHXBuffer spTrackURLBuffer; pTrackProperties->GetPropertyCString( kURLPropertyName, *spTrackURLBuffer.AsInOutParam() ); if ( !spTrackURLBuffer.IsValid() ) { pTrackProperties->GetPropertyCString( kSourcePropertyName, *spTrackURLBuffer.AsInOutParam() ); } if ( spTrackURLBuffer.IsValid() ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -