基于directshow的播放器的附加功能

大学的小学期做了一个基于directshow的播放器,主要算法来源于前人的著作,而csdn中也有很详细的注解:http://blog.csdn.net/harvic880925/article/details/7978566

之后说一下自己的附加部分。基于windowSDK中directshow和MFC,使用vs2013作为开发工具。

一、DirectShow简介

DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,9.0之前与DirectX开发包一起发布,之后包含在windows SDK中。

运用DirectShow,我们可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。它广泛地支持各种媒体格式,包括Asf、Mpeg、avi、dv、Mp3、Wave等等,使得多媒体数据的回放变得轻而易举。另外,DirectShow还集成了DirectX其它部分(比如DirectDraw、DirectSound)的技术,直接支持DVD的播放,视频的非线性编辑,以及与数字摄像机的数据交换。

二、多媒体播放器功能分析

(1)本地播放

     打开本地文件,安装解码器后可以支持绝大部分媒体格式。打开通过获取文件地址,赋给m_sourcefile,之后调用底层函数进行播放。

bool CPlayerDlg::Run()

{

    if(m_Graph&&this->m_MediaControl)

    {

        if(!IsRunning())

        {

            if(SUCCEEDED(this->m_MediaControl->Run()))

            {

                return true;

            }

        }else

        {

            return true;

        }

    }

    return false;

}

(2)暂停与停止

     通过调用pause()和stop()函数实现暂停和停止功能。

bool CPlayerDlg::Pause()

{

    if(m_Graph&&this->m_MediaControl)

    {

        if(!this->IsPaused())

        {

            if(SUCCEEDED(this->m_MediaControl->Pause()))

            {

                return true;

            }

        }else

        {

            return true;

        }

    }

    return false;

}

bool CPlayerDlg::Stop()

{

    if(m_Graph&&this->m_MediaControl)

    {

        if(!this->IsStopped())

        {

            if(SUCCEEDED(this->m_MediaControl->Stop()))

            {

                return true;

            }

        }else

        {

            return true;

        }

    }

    return false;

}

(3)快进与快退

     首先调用duration()函数获取总的时间,之后在这一基础上加上几秒或减去几秒。再从新的位置播放。

void CPlayerDlg::OnBnClickedquickback()

{

    //TODO:  在此添加控件通知处理程序代码

    double duration = 1;

    double pos = 0;

    pos = this->m_Slider.GetPos();

    this->GetDuration(&duration);

    double newPos = (duration*pos - 5000) / 1000;

    if (newPos >= 0 && (newPos <= duration))

        this->SetCurrentPosition(newPos);

    else

    {

        this->SetCurrentPosition(0);

    }

}

(4)变速率播放

获取控件的值,可以调用IMediaSeeking类中的方法setrate(),改变媒体播放速率。

void CPlayerDlg::SetRate()

{

    int nIndex = m_cbExamble.GetCurSel();

    if (nIndex==0)

    {

        m_Seeking->SetRate(0.5);

    }

    else if (nIndex == 1)

    {

        m_Seeking->SetRate(1);

    }

    else if (nIndex==2)

    {

        m_Seeking->SetRate(1.5);

    }

    else

    {

        m_Seeking->SetRate(1);

    }

   

}

(5)音量调节

   音量调节的原理即获取滑动条的进度值,之后计算相应音量的值,即可实现音量控制。因为这也属于横向滑动条的响应函数,即加入到控制进度的滑动条消息中。

    else if (pScrollBar->GetSafeHwnd() == m_volume.GetSafeHwnd())

    {

        if (this->m_volume)

        {

 

            int pos= mxc.Bounds.dwMaximum - m_volume.GetPos();

           

            volStruct.lValue = pos; //想要设置的值

            mixerSetControlDetails((HMIXEROBJ)m_hmx,&mxcd,

                MIXER_SETCONTROLDETAILSF_VALUE);

        }

(6)进度控制

   通过duration函数获取总的时间,之后乘以从进度条读取的进度百分比,即可计算出现在的时间,从而获取现在的播放位置。

if(pScrollBar->GetSafeHwnd()==this->m_Slider.GetSafeHwnd())

    {

        if(this->m_Slider)

        {

            doubleduration=1;

            doublepos=0;

            pos=this->m_Slider.GetPos();

            this->GetDuration(&duration);

            doublenewPos=duration*pos/1000;

            this->SetCurrentPosition(newPos);

        }

    }

(7)播放列表与历史记录

播放列表为一个listbox,在点击打开文件后,可使用addstring()方法将名字加入到listbox中,同时将其写入文件中作为历史记录。

在打开软件时从文件读入信息,从而将地址和名字赋给结构数组pathinformation,方便之后查找。

当双击listbox时触发响应,从而获取该行的字符串,与pathinformation中的数据进行比对即可得到URL地址。

void CPlayerDlg::writefile(URLinf &URLinformation)

{

    CString a1=URLinformation.name;

    CString a2 = URLinformation.URL;

 

    CStdioFile file(_T("infname.txt"),CFile::modeCreate |CFile::modeNoTruncate |CFile::modeReadWrite);

    file.SeekToEnd();

    char* old_locale = _strdup(setlocale(LC_CTYPE,NULL));

    setlocale(LC_CTYPE,"chs");//设定

    file.WriteString(a1+"\n");//正常写入

    setlocale(LC_CTYPE,old_locale);

    free(old_locale);//还原区域设定

    file.Close();

 

    CStdioFile file1(_T("infpath.txt"),CFile::modeCreate |CFile::modeNoTruncate |CFile::modeReadWrite);

    file1.SeekToEnd();

    char* old_locale1 = _strdup(setlocale(LC_CTYPE,NULL));

    setlocale(LC_CTYPE,"chs");//设定

    file1.WriteString(a2+"\n");//正常写入

    setlocale(LC_CTYPE,old_locale1);

    free(old_locale1);//还原区域设定

    file1.Close();

   

}

void CPlayerDlg::readfile()

{

   

    int i = 0;

    int j = 0;

    CStdioFile  File;

    CStdioFile  File1;

    CString a1;

    CString a2;

    TCHAR buf[1000];

    TCHAR buf1[1000];

    if (File.Open(_T("infname.txt"),CFile::modeNoTruncate|CFile::modeRead))

    {

 

        char*old_locale = _strdup(setlocale(LC_CTYPE,NULL));

        setlocale(LC_CTYPE,"chs");//设定

        while(File.ReadString(buf, 1000))

        {

            m_list.AddString(buf);

            pathInformation[i].name = buf;

            pathInformation[i].name.Trim(_T("\n"));

            i++;

        }

        setlocale(LC_CTYPE,old_locale);

        free(old_locale);//还原区域设定

    }

    File.Close(); /*关闭文件*/

 

    if (File1.Open(_T("infpath.txt"),CFile::modeNoTruncate |CFile::modeRead))

    {

 

        char*old_locale1 = _strdup(setlocale(LC_CTYPE,NULL));

        setlocale(LC_CTYPE,"chs");//设定

        while(File1.ReadString(buf1, 1000))

        {

            pathInformation[j].URL = buf1;

            pathInformation[j].URL.Trim(_T("\n"));

            j++;

        }

        setlocale(LC_CTYPE,old_locale1);

        free(old_locale1);//还原区域设定

    }

    File1.Close();

}

 

void CPlayerDlg::insertfile(URLinf &URLinformation)

{

    int change = 0;

    int cc = 0;

    int length = m_list.GetCount();

    for (int i = 0; i <=length; i++)

    {

        if (URLinformation.name==pathInformation[i].name)

        {

            change = 0;

        }

        else

        {

            change = 1;

            cc = i;

        }

    }

    if (change == 1)

    {

        writefile(URLinformation);

        m_list.AddString(URLinformation.name);

        pathInformation[cc].name = URLinformation.name;

        pathInformation[cc].URL = URLinformation.URL;

    }

}

 

 

void CPlayerDlg::OnLbnDblclkList2()

{

    //TODO:  在此添加控件通知处理程序代码

    int record=0;

    int judge = m_list.GetCurSel();

    int length = m_list.GetCount();

    CString temp="";

    m_list.GetText(judge,temp);

    temp.Trim(_T("\n"));

    for (int i = 0; i < length;i++)

    if (pathInformation[i].name==temp)

    {

        record = i;

    }

    else

    {

        ;

    }

    m_SourceFile = pathInformation[record].URL;

    this->CreateGraph();

}

(8)全屏

首先记录各个控件的位置,方便之后还原。然后重新画窗体,设置各个控件的位置和大小,同时监测如果有鼠标双击则恢复初始状态。

bool CPlayerDlg::SetFullScreen(bool inEnabled)

{

    if(m_Graph&&this->m_VideoWindow)

    {

        if(SUCCEEDED(this->m_VideoWindow->put_FullScreenMode(inEnabled ?OATRUE : OAFALSE)))

        {

            return true;

        }

    }

    return false;

}

//这里同样是一条函数的封装,我想三目运算符,大家应该还记得吧,这里就不讲了哦

bool CPlayerDlg::GetFullScreen(void)

{

    if (m_VideoWindow)

    {

        long  fullScreenMode = OAFALSE;

        m_VideoWindow->get_FullScreenMode(&fullScreenMode);

        return(fullScreenMode == OATRUE);

    }

    return false;

}

//这个函数难度不大,应该没什么可讲的吧

bool CPlayerDlg::SetDisplayWindow(HWNDHWindow)

{

    if(this->m_VideoWindow)

    {

        this->m_VideoWindow->put_Visible(OAFALSE);//首先隐藏窗口

 

        if(HWindow)

        {

            this->m_VideoWindow->put_Owner(OAHWND(HWindow));

 

            CRectwndRect;

            ::GetClientRect(HWindow,&wndRect);//一定要注意要用WND坐标,而不能桌面坐标

            this->m_VideoWindow->put_Left(0);

            this->m_VideoWindow->put_Top(0);

            this->m_VideoWindow->put_Height((long)wndRect.Height());

            this->m_VideoWindow->put_Width((long)wndRect.Width()); //定义显示窗口位置

 

            this->m_VideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);//WS_CLIPSIBLINGS表示重绘时,只重绘这一个窗口,其它窗口不发生重绘。

                                                                                           //WS_CLIPCHILDREN表示此窗口不允许被其它窗口覆盖

            this->m_VideoWindow->put_MessageDrain((OAHWND)HWindow);//设置接收鼠标键盘的窗口

 

            this->m_VideoWindow->put_Visible(OATRUE);

 

            return true;

        }

    }

    return false;

}

 

bool CPlayerDlg::SetFullScreen2()

{

    if(m_bFullScreen)

    {

        ModifyStyle(WS_POPUP,WS_CAPTION);

        //首先还原框架的大小及位置

        SetWindowPlacement(&m_OldWndPlacement);

 

        //将播放窗口还原到原来的位置

    this->m_VideoWindowPlay.SetWindowPos(&wndTop,m_OldPlayWndRect.left,m_OldPlayWndRect.top,m_OldPlayWndRect.Width(),m_OldPlayWndRect.Height(),SWP_NOACTIVATE);

        //还原视频显示大小

        this->SetDisplayWindow(m_VideoWindowPlay.GetSafeHwnd());

        //还原其它控件的位置

    this->GetDlgItem(IDC_PROGRESS)->MoveWindow(m_OldProgressRect.left,m_OldProgressRect.top,m_OldProgressRect.Width(),m_OldProgressRect.Height(),true);

    this->GetDlgItem(IDC_BTN_OPEN)->MoveWindow(m_OldOpenBtnRect.left,m_OldOpenBtnRect.top,m_OldOpenBtnRect.Width(),m_OldOpenBtnRect.Height(),true);

    this->GetDlgItem(IDC_BTN_PLAY)->MoveWindow(m_OldPlayBtnRect.left,m_OldPlayBtnRect.top,m_OldPlayBtnRect.Width(),m_OldPlayBtnRect.Height(),true);

    this->GetDlgItem(IDC_BTN_PAUSE)->MoveWindow(m_OldPauseBtnRect.left,m_OldPauseBtnRect.top,m_OldPauseBtnRect.Width(),m_OldPauseBtnRect.Height(),true);

    this->GetDlgItem(IDC_BTN_STOP)->MoveWindow(m_OldStopBtnRect.left,m_OldStopBtnRect.top,m_OldStopBtnRect.Width(),m_OldStopBtnRect.Height(),true);

        this->GetDlgItem(IDC_quickForward)->MoveWindow(m_OldquickForward.left,m_OldquickForward.top, m_OldquickForward.Width(), m_OldquickForward.Height(),true);

        this->GetDlgItem(ID_quickBack)->MoveWindow(m_OldquickBack.left,m_OldquickBack.top, m_OldquickBack.Width(), m_OldquickBack.Height(),true );

        this->GetDlgItem(IDC_STATIC2)->MoveWindow(m_OldSTATIC2.left,m_OldSTATIC2.top, m_OldSTATIC2.Width(), m_OldSTATIC2.Height(),true);

        this->GetDlgItem(IDC_STATIC3)->MoveWindow(m_OldSTATIC3.left,m_OldSTATIC3.top, m_OldSTATIC3.Width(), m_OldSTATIC3.Height(),true);

        this->GetDlgItem(IDC_STATIC4)->MoveWindow(m_OldSTATIC4.left,m_OldSTATIC4.top, m_OldSTATIC4.Width(), m_OldSTATIC4.Height(),true);

        this->GetDlgItem(IDC_SLIDER1)->MoveWindow(m_OldSLIDER1.left,m_OldSLIDER1.top, m_OldSLIDER1.Width(), m_OldSLIDER1.Height(),true);

        this->GetDlgItem(IDC_controlrate)->MoveWindow(m_Oldcontrolrate.left,m_Oldcontrolrate.top, m_Oldcontrolrate.Width(), m_Oldcontrolrate.Height(),true);

        this->GetDlgItem(IDC_LIST2)->MoveWindow(m_OldLIST2.left,m_OldLIST2.top, m_OldLIST2.Width(), m_OldLIST2.Height(),true);

        m_bFullScreen=false;

        return true;

   

    }else

    {

        //先放大总框架

        GetWindowPlacement(&m_OldWndPlacement);

        CRectm_RectOfCurrentWindow,m_RectOfClient;

        GetWindowRect(&m_RectOfCurrentWindow);

        RepositionBars(0,0xffff,AFX_IDW_PANE_FIRST,reposQuery,&m_RectOfClient);

        ClientToScreen(&m_RectOfClient);

        intnFullWidth = GetSystemMetrics(SM_CXSCREEN);

        intnFullHeight = GetSystemMetrics(SM_CYSCREEN);

        CRectm_FSRect;

        m_FSRect.left =m_RectOfCurrentWindow.left-m_RectOfClient.left;

        m_FSRect.top = m_RectOfCurrentWindow.top- m_RectOfClient.top;

        m_FSRect.right =m_RectOfCurrentWindow.right - m_RectOfClient.right+nFullWidth;

        m_FSRect.bottom =m_RectOfCurrentWindow.bottom - m_RectOfClient.bottom + nFullHeight;

        MoveWindow(&m_FSRect,TRUE);

 

       

 

        //先保存播放窗口及各控件原来的位置,以便还原

        this->m_VideoWindowPlay.GetWindowRect(this->m_OldPlayWndRect);

        this->GetDlgItem(IDC_PROGRESS)->GetWindowRect(m_OldProgressRect);

        this->GetDlgItem(IDC_BTN_OPEN)->GetWindowRect(m_OldOpenBtnRect);

        this->GetDlgItem(IDC_BTN_PLAY)->GetWindowRect(m_OldPlayBtnRect);

        this->GetDlgItem(IDC_BTN_PAUSE)->GetWindowRect(m_OldPauseBtnRect);

        this->GetDlgItem(IDC_BTN_STOP)->GetWindowRect(m_OldStopBtnRect);

        this->GetDlgItem(IDC_quickForward)->GetWindowRect(m_OldquickForward);

        this->GetDlgItem(ID_quickBack)->GetWindowRect(m_OldquickBack);

        this->GetDlgItem(IDC_STATIC2)->GetWindowRect(m_OldSTATIC2);

        this->GetDlgItem(IDC_STATIC3)->GetWindowRect(m_OldSTATIC3);

        this->GetDlgItem(IDC_STATIC4)->GetWindowRect(m_OldSTATIC4);

        this->GetDlgItem(IDC_SLIDER1)->GetWindowRect(m_OldSLIDER1);

        this->GetDlgItem(IDC_controlrate)->GetWindowRect(m_Oldcontrolrate);

        this->GetDlgItem(IDC_LIST2)->GetWindowRect(m_OldLIST2);

   

 

        CRectrect;

        ::GetWindowRect(::GetDesktopWindow(),rect);

        //设置作为播放窗口的图片控件的位置

        this->m_VideoWindowPlay.SetWindowPos(&wndTop,rect.left,rect.top,rect.Width(),rect.Height()-100,SWP_NOACTIVATE);

        //设置视频图像显示输出的大小

        this->m_VideoWindow->put_Left(rect.left);

        this->m_VideoWindow->put_Top(rect.top);

        this->m_VideoWindow->put_Width(rect.right   -  rect.left);

        this->m_VideoWindow->put_Height(rect.bottom   -   rect.top);//播放窗口最大化

 

        //设置其它控件新的位置

        this->GetDlgItem(IDC_PROGRESS)->MoveWindow(rect.left,rect.bottom-90,rect.Width(),20,true);

        CRectrectTemp;

        this->GetDlgItem(IDC_BTN_OPEN)->GetWindowRect(rectTemp);

        this->GetDlgItem(IDC_BTN_OPEN)->MoveWindow(rect.left+rect.Width()/2-600,rect.bottom-50,rectTemp.Width(),rectTemp.Height(),true);

        this->GetDlgItem(IDC_BTN_PLAY)->MoveWindow(rect.left+rect.Width()/2-500,rect.bottom-50,rectTemp.Width(),rectTemp.Height(),true);

        this->GetDlgItem(IDC_BTN_PAUSE)->MoveWindow(rect.left+rect.Width()/2-400,rect.bottom-50,rectTemp.Width(),rectTemp.Height(),true);

        this->GetDlgItem(IDC_BTN_STOP)->MoveWindow(rect.left+rect.Width()/2-300,rect.bottom-50,rectTemp.Width(),rectTemp.Height(),true);

        this->GetDlgItem(IDC_quickForward)->MoveWindow(rect.left+ rect.Width() / 2 -200, rect.bottom - 50, rectTemp.Width(), rectTemp.Height(),true);

        this->GetDlgItem(ID_quickBack)->MoveWindow(rect.left+ rect.Width() / 2 -100, rect.bottom - 50, rectTemp.Width(), rectTemp.Height(),true);

        this->GetDlgItem(IDC_STATIC2)->MoveWindow(rect.left+ rect.Width() / 2 +100, rect.bottom - 50, rectTemp.Width(), rectTemp.Height(),true);

        this->GetDlgItem(IDC_STATIC3)->MoveWindow(rect.left+ rect.Width() / 2 +300, rect.bottom - 50, rectTemp.Width(), rectTemp.Height(),true);

        this->GetDlgItem(IDC_STATIC4)->MoveWindow(rect.left+ rect.Width() / 2 +500, rect.bottom - 50, rectTemp.Width(), rectTemp.Height(),true);

        this->GetDlgItem(IDC_SLIDER1)->MoveWindow(rect.left+ rect.Width() / 2 +150, rect.bottom - 50, rectTemp.Width(), rectTemp.Height(),true);

        this->GetDlgItem(IDC_controlrate)->MoveWindow(rect.left+ rect.Width() / 2 +350, rect.bottom -50, rectTemp.Width(), rectTemp.Height(),true);

        this->GetDlgItem(IDC_LIST2)->MoveWindow(rect.left+ rect.Width() / 2 +600, rect.bottom - 50, rectTemp.Width(), rectTemp.Height(),true);

 

        ShowCaret();//显示鼠标 

 

        m_bFullScreen=true;

        return true;       

    }

    return false;

}

三、问题及解决方法

(1)安装配置开发环境,由于directshow已经集成到windows SDK 中,安装的时候往往无法正确安装,解决方法:安装子文件夹中的任意msi文件,之后再安装setup即可。配置开发环境时先要编译baseclasses,得到debug文件夹。之后在工程中需要包含路径,也出现了一些失误。

(2)在读写文件时不熟悉cstuiofile类,导致未能正确使用属性和文件读写出现许多失误,包括不能读取中文等。后来认真学习此类,又在网上找了许多资料完成了这一工作。

(3)全屏和恢复是一项繁琐的工作,如果不细心往往会出现差错。

代码可以下载:http://download.csdn.net/detail/u012483487/8463807

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值