分享
 
 
 

怎样抓CD音轨存为WMA文件

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

前言:

在CSDN上也耗了很久,但是每次都是看人家的文章或帖子,奉行的都是“拿来主义”,从来没有自己的文章发表,有了blog,我也有机会了,但是没有什么含金量的文章,也不知写哪方面的东西,前段时间在论坛上发了个帖子,主要是CD转WMA方面的,但是帖子很快就沉了,因为没有几个人知道。经过自己的努力,终于做出来了,鉴于很多人还不知道怎么作,所以斗胆在此乱发一通,希望高手还望见谅!

正文:

网上有CD to WAV的文章和源码。但wav和wma还是有很大不同,我是参考了怎样读CD信息的相关代码,然后自己实现了一个CCDAudioInput的类,该类封装了CD的信息和读取CD数据的相关操作。主要的难点是怎样把CD数据存入wma文件里,因为wma是Microsoft的asf文件格式,所以我看了Microsoft的windows media SDK和里面的samples。windows media SDK主要是以com组件实现的开发库,个人觉得只要大概了解com应该问题不大。为了方便Wma的操作我设计了一个CWmvWriter的类,里面包括了对wma文件的写入操作以及一些初始化工作。有了这两个类,我们就可以轻而易举的实现我们的功能。最后我们用一个button调用我么CD to wma的功能。在调用之前一定要现调用 hr = CoInitialize( NULL )用于初始化。下面把CCAudioInput和CWmvWriter类的主要部分贴出。

CCAudioInput.h:

#pragma once

#include <ntddcdrm.h> // NT4DDK库

#include <winioctl.h>

#include <Mmreg.h>

// CCDAudioInput command target

#define NSECTORS 13

#define UNDERSAMPLING 1

#define CB_CDDASECTOR 2368

#define CB_QSUBCHANNEL 16

#define CB_CDROMSECTOR 2048

#define CB_AUDIO (CB_CDDASECTOR-CB_QSUBCHANNEL)

class CCDAudioInput

{

public:

CStringArray m_Tracks; // the array of audio track

CStringArray m_TrackTitles; // the array of track title

CString m_sArtist; // the cd's artist

CString m_sDrive; // the CD-ROM drive name

HANDLE m_hDevice; // the Device Handle of CD-ROM

CDROM_TOC CdromTOC;

public:

CCDAudioInput(CString &Drive);

CCDAudioInput(const CCDAudioInput &cdInfo);

CCDAudioInput();

virtual ~CCDAudioInput();

// Decide the CDROM if is ready

BOOL IsReady(void);

// Get the end of sector

DWORD GetEndSector(int nTrack);

// Get the start sector

DWORD GetStartSector(int nTrack);

// Read sector data to Buffer from CD-ROM

BOOL ReadSector(int Sector, BYTE Buffer[], int NumSectors);

CCDAudioInput& operator=(const CCDAudioInput &m_cdInfo);

};

CCAudioInput.cpp:

#include "stdafx.h"

#include "CDAudioInput.h"

#include ".\cdaudioinput.h"

// CCDAudioInput

// CCDAudioInput member functions

CCDAudioInput::CCDAudioInput()

{

m_sArtist = "";

m_hDevice = INVALID_HANDLE_VALUE;

m_Tracks.RemoveAll();

}

CCDAudioInput::CCDAudioInput(CString &Drive)

{

m_sDrive = Drive;

m_hDevice = CreateFile("\\\\.\\"+m_sDrive, GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,

OPEN_EXISTING, 0, NULL);

}

CCDAudioInput::CCDAudioInput(const CCDAudioInput &cdInfo)

{

m_sDrive = cdInfo.m_sDrive;

m_hDevice = cdInfo.m_hDevice;

m_sArtist = cdInfo.m_sArtist;

m_Tracks.Copy(cdInfo.m_Tracks);

}

CCDAudioInput::~CCDAudioInput()

{

}

CCDAudioInput& CCDAudioInput::operator=(const CCDAudioInput &m_cdInfo)

{

if(this != &m_cdInfo)

{

m_sDrive = m_cdInfo.m_sDrive;

m_hDevice = m_cdInfo.m_hDevice;

m_sArtist = m_cdInfo.m_sArtist;

m_Tracks.Copy(m_cdInfo.m_Tracks);

}

return *this;

}

// Decide the CDROM if is ready

BOOL CCDAudioInput::IsReady(void)

{

DWORD dwOutBytes;

return (DeviceIoControl(m_hDevice,IOCTL_STORAGE_CHECK_VERIFY, NULL, 0,NULL, 0, &dwOutBytes, (LPOVERLAPPED)NULL));

}

// Get the end of sector

DWORD CCDAudioInput::GetEndSector(int nTrack)

{

return (CdromTOC.TrackData[nTrack].Address[1]*60*75 + CdromTOC.TrackData[nTrack].Address[2]*75 +

CdromTOC.TrackData[nTrack].Address[3])-151;

}

// Get the start sector

DWORD CCDAudioInput::GetStartSector(int nTrack)

{

return (CdromTOC.TrackData[nTrack-1].Address[1]*60*75 + CdromTOC.TrackData[nTrack-1].Address[2]*75 +

CdromTOC.TrackData[nTrack-1].Address[3])-150;

}

BOOL CCDAudioInput::ReadSector(int Sector, BYTE Buffer[], int NumSectors)

{

DWORD dwOutBytes;

RAW_READ_INFO rri;

rri.TrackMode =(TRACK_MODE_TYPE)2;

rri.SectorCount = (DWORD)NumSectors;

rri.DiskOffset.QuadPart = (DWORD64)(Sector*CB_CDROMSECTOR);

if (DeviceIoControl(m_hDevice,IOCTL_CDROM_RAW_READ,&rri, sizeof(rri), Buffer, (DWORD)NumSectors*CB_AUDIO,&dwOutBytes,(LPOVERLAPPED)NULL))

return TRUE;

return FALSE;

}

CWmvWriter.h:

#pragma once

// CWmvWriter command target

#include "wmsdk.h" // windows media SDK header file

#include "wmsdkidl.h" // windows media SDK header file

#include <Mmreg.h>

#include "wmsysprf.h" // windows media SDK header file

#ifndef SAFE_RELEASE

#define SAFE_RELEASE( x ) if ( NULL != x ) { x->Release( ); x = NULL; }

#endif // SAFE_RELEASE

#ifndef SAFE_ARRAYDELETE

#define SAFE_ARRAYDELETE( x ) if( x ) { delete [] x; x = NULL; }

#endif //SAFE_ARRAYDELETE

const LPWSTR wszDefaultConnectionName = L"CDTOWMA";

typedef struct MediaTypeTAG{

WAVEFORMATEX m_pWFX;

GUID m_Type;

}MEDIATYPE, *PMEDIATYPE;

class CWmvWriter : public CObject

{

public:

CWmvWriter();

virtual ~CWmvWriter();

HRESULT Initial(TCHAR* ptszInFile, TCHAR* ptszOutFile, IWMProfile* pProfile);

HRESULT AddAudioStream(IWMProfile * pIWMProfile, DWORD dwSampleRate, DWORD dwChannels, WORD wBitsPerSample, WORD* pwStreamNum, WCHAR ** pwszConnectionName);

HRESULT UpdateProfile(IWMProfile * pProfile);

public:

IWMWriter *m_pWMWriter;

WM_MEDIA_TYPE mt;

// WAVEFORMATEX *m_pWFX;

MEDIATYPE m_MediaType;

HRESULT SetStreamBasics(IWMStreamConfig * pIWMStreamConfig, IWMProfile * pIWMProfile, LPWSTR pwszStreamName, LPWSTR pwszConnectionName, DWORD dwBitrate, WM_MEDIA_TYPE * pmt);

HRESULT CreateEmptyProfile(IWMProfile ** ppIWMProfile);

HRESULT WriteSample(BYTE* pBuffer, DWORD Length);

DWORD m_AudioInput;

DWORD m_MsAudioTime;

};

CWmvWriter.cpp:

// WmvWriter.cpp : implementation file

#include "stdafx.h"

#include "WmvWriter.h"

#include ".\wmvwriter.h"

// CWmvWriter

CWmvWriter::CWmvWriter()

: m_AudioInput(0)

, m_MsAudioTime(0)

{

m_pWMWriter = NULL;

ZeroMemory(&m_MediaType, sizeof(MEDIATYPE));

}

CWmvWriter::~CWmvWriter()

{

}

HRESULT ConvertTCharToWChar( TCHAR * ptszInput, WCHAR ** pwszOutput )

{

int cchOutput = 0;

if( NULL == ptszInput || NULL == pwszOutput )

{

return( E_INVALIDARG );

}

//

// Get output buffer size

//

#ifdef UNICODE

cchOutput = wcslen( ptszInput ) + 1;

#else //UNICODE

cchOutput = MultiByteToWideChar( CP_ACP, 0, ptszInput, -1, NULL, 0 );

if( 0 == cchOutput )

{

return( HRESULT_FROM_WIN32( GetLastError() ) );

}

#endif // UNICODE

*pwszOutput = new WCHAR[ cchOutput ];

if( NULL == *pwszOutput)

{

return( E_OUTOFMEMORY );

}

#ifdef UNICODE

wcsncpy( *pwszOutput, ptszInput, cchOutput );

#else //UNICODE

if( 0 == MultiByteToWideChar( CP_ACP, 0, ptszInput, -1, *pwszOutput, cchOutput ) )

{

SAFE_ARRAYDELETE( *pwszOutput );

return( HRESULT_FROM_WIN32( GetLastError() ) );

}

#endif // UNICODE

return( S_OK );

}

// CWmvWriter member functions

HRESULT CWmvWriter::Initial(TCHAR* ptszInFile, TCHAR* ptszOutFile, IWMProfile* pProfile)

{

HRESULT hr = S_OK;

if((ptszOutFile == NULL) || (pProfile == NULL))

return E_INVALIDARG;

hr = WMCreateWriter(NULL, &m_pWMWriter);

if(FAILED(hr))

{

AfxMessageBox("Failed to Create WMWriter.");

return (hr);

}

hr = UpdateProfile(pProfile);

if(FAILED(hr))

{

AfxMessageBox("Failed to Call UpdateProfile.");

return (hr);

}

hr = m_pWMWriter->SetProfile(pProfile);

if(FAILED(hr))

{

AfxMessageBox("Failed to Call SetProfile.");

return hr;

}

if( NULL != ptszOutFile )

{

WCHAR * pwszOutFile = NULL;

hr = ConvertTCharToWChar( ptszOutFile, &pwszOutFile );

hr = m_pWMWriter->SetOutputFilename( pwszOutFile );

SAFE_ARRAYDELETE( pwszOutFile );

if(FAILED(hr))

{

AfxMessageBox("Failed to Call SetOutputFilename.");

return (hr);

}

}

return hr;

}

HRESULT CWmvWriter::UpdateProfile(IWMProfile * pProfile)

{

HRESULT hr = S_OK;

if(pProfile == NULL)

return E_INVALIDARG;

m_MediaType.m_Type = WMMEDIATYPE_Audio;

m_MediaType.m_pWFX.wFormatTag=WAVE_FORMAT_PCM ;

m_MediaType.m_pWFX.nSamplesPerSec=48000;

m_MediaType.m_pWFX.wBitsPerSample=16;

m_MediaType.m_pWFX.nChannels=2;

m_MediaType.m_pWFX.cbSize=0;

m_MediaType.m_pWFX.nBlockAlign = m_MediaType.m_pWFX.nChannels*(m_MediaType.m_pWFX.wBitsPerSample/8);

m_MediaType.m_pWFX.nAvgBytesPerSec = m_MediaType.m_pWFX.nSamplesPerSec*m_MediaType.m_pWFX.nBlockAlign;

WORD wStreamNum = 0;

WCHAR *pwszConnectionName;

hr = AddAudioStream( pProfile, m_MediaType.m_pWFX.nSamplesPerSec, m_MediaType.m_pWFX.nChannels

, m_MediaType.m_pWFX.wBitsPerSample, &wStreamNum, &pwszConnectionName);

if(FAILED(hr))

{

AfxMessageBox("Failed to Call AddAudioStream.");

return hr;

}

return hr;

}

HRESULT CWmvWriter::AddAudioStream(IWMProfile * pIWMProfile, DWORD dwSampleRate, DWORD dwChannels,

WORD wBitsPerSample, WORD* pwStreamNum, WCHAR ** pwszConnectionName)

{

HRESULT hr = S_OK;

IWMProfileManager * pIWMProfileManager = NULL;

IWMStreamConfig * pIWMStreamConfig = NULL;

IWMMediaProps * pIMP = NULL;

IWMCodecInfo * pIWMInfo = NULL;

WAVEFORMATEX * pWfx = NULL;

WM_MEDIA_TYPE * pType = NULL;

if((NULL == pIWMProfile) || (NULL == pwStreamNum))

return E_INVALIDARG;

do{

hr = WMCreateProfileManager(&pIWMProfileManager);

if(FAILED(hr)) break;

hr = pIWMProfileManager->QueryInterface(IID_IWMCodecInfo, (void**) &pIWMInfo);

if(FAILED(hr)) break;

DWORD i, j;

DWORD cCodecs;

hr = pIWMInfo->GetCodecInfoCount(WMMEDIATYPE_Audio, &cCodecs);

if(FAILED(hr)) break;

for(i=0; i<cCodecs; i++)

{

DWORD cFormats;

hr = pIWMInfo->GetCodecFormatCount( WMMEDIATYPE_Audio, i, &cFormats );

if( FAILED( hr ) ) break;

// Find a proper format in this codec

for(j=0; j<cFormats; j++)

{

if( NULL != pType )

SAFE_ARRAYDELETE( pType );

DWORD cbType = 0;

hr = pIWMInfo->GetCodecFormat( WMMEDIATYPE_Audio, i, j, &pIWMStreamConfig );

if(FAILED(hr)) break;

SAFE_RELEASE( pIMP );

hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps, (void **)&pIMP );

if(FAILED(hr)) break;

hr = pIMP->GetMediaType( NULL, &cbType );

if(FAILED(hr)) break;

pType = (WM_MEDIA_TYPE *) new BYTE[ cbType ];

if( NULL == pType )

{

hr = E_OUTOFMEMORY;

break;

}

hr = pIMP->GetMediaType( pType, &cbType );

if(FAILED(hr)) break;

if( pType->formattype != WMFORMAT_WaveFormatEx )

{

hr = E_FAIL;

break;

}

pWfx = (WAVEFORMATEX *) pType->pbFormat;

if( pWfx->nSamplesPerSec == dwSampleRate && pWfx->nChannels == dwChannels &&

pWfx->wBitsPerSample >= wBitsPerSample )

{

break;

}

SAFE_RELEASE( pIWMStreamConfig );

}

if( FAILED( hr ) || NULL != pIWMStreamConfig )

break;

}

if(FAILED(hr)) break;

if( NULL == pIWMStreamConfig )

{

hr = NS_E_AUDIO_CODEC_NOT_INSTALLED;

break;

}

hr = SetStreamBasics( pIWMStreamConfig,

pIWMProfile,

L"Audio Stream",

L"Audio",

pWfx->nAvgBytesPerSec * 8,

pType );

if( FAILED( hr ) )

{

break;

}

*pwszConnectionName = new WCHAR[ wcslen( wszDefaultConnectionName ) + 4 ];

if( NULL == *pwszConnectionName )

{

hr = E_OUTOFMEMORY;

break;

}

hr = pIWMProfile->AddStream( pIWMStreamConfig );

if( FAILED( hr ) )

{

SAFE_ARRAYDELETE( *pwszConnectionName );

break;

}

hr = pIWMStreamConfig->GetStreamNumber( pwStreamNum );

if( FAILED( hr ) )

{

SAFE_ARRAYDELETE( *pwszConnectionName );

break;

}

//

// each stream in the profile has to have unique connection name

// let's use stream number to create it

//

if( *pwStreamNum > 127 )

{

hr = E_FAIL;

break;

}

swprintf( *pwszConnectionName, L"%s%d", wszDefaultConnectionName, (DWORD)*pwStreamNum );

hr = pIWMStreamConfig->SetConnectionName( *pwszConnectionName );

if( FAILED( hr ) )

{

SAFE_ARRAYDELETE( *pwszConnectionName );

break;

}

hr = pIWMProfile->ReconfigStream( pIWMStreamConfig );

if( FAILED( hr ) )

{

SAFE_ARRAYDELETE( *pwszConnectionName );

break;

}

}

while( FALSE );

SAFE_ARRAYDELETE( pType );

SAFE_RELEASE( pIWMInfo );

SAFE_RELEASE( pIWMStreamConfig );

SAFE_RELEASE( pIMP );

SAFE_RELEASE( pIWMProfileManager );

return( hr );

}

HRESULT CWmvWriter::SetStreamBasics(IWMStreamConfig *pIWMStreamConfig, IWMProfile *pIWMProfile, LPWSTR pwszStreamName,

LPWSTR pwszConnectionName, DWORD dwBitrate, WM_MEDIA_TYPE * pmt)

{

HRESULT hr = S_OK;

IWMMediaProps * pIWMMediaProps = NULL;

IWMStreamConfig * pIWMStreamConfig2 = NULL;

WORD wStreamNum = 0;

if( NULL == pIWMStreamConfig || NULL == pIWMProfile || NULL == pmt )

{

return( E_INVALIDARG );

}

do

{

hr = pIWMProfile->CreateNewStream( pmt->majortype, &pIWMStreamConfig2 );

if( FAILED( hr ) )

{

break;

}

hr = pIWMStreamConfig2->GetStreamNumber( &wStreamNum );

SAFE_RELEASE( pIWMStreamConfig2 );

if( FAILED( hr ) )

{

break;

}

hr = pIWMStreamConfig->SetStreamNumber( wStreamNum );

if( FAILED( hr ) )

{

break;

}

hr = pIWMStreamConfig->SetStreamName( pwszStreamName );

if( FAILED( hr ) )

{

break;

}

hr = pIWMStreamConfig->SetConnectionName( pwszConnectionName );

if( FAILED( hr ) )

{

break;

}

hr = pIWMStreamConfig->SetBitrate( dwBitrate );

if( FAILED( hr ) )

{

break;

}

hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps,

(void **) &pIWMMediaProps );

if( FAILED( hr ) )

{

break;

}

hr = pIWMMediaProps->SetMediaType( pmt );

if( FAILED( hr ) )

{

break;

}

}

while( FALSE );

SAFE_RELEASE( pIWMMediaProps );

return( hr );

}

HRESULT CWmvWriter::CreateEmptyProfile(IWMProfile ** ppIWMProfile)

{

HRESULT hr = S_OK;

IWMProfileManager* pIWMProfileManager = NULL;

if( NULL == ppIWMProfile )

{

return( E_POINTER );

}

do

{

//

// Create profile manager

//

hr = WMCreateProfileManager( &pIWMProfileManager );

if( FAILED( hr ) )

{

break;

}

hr = pIWMProfileManager->CreateEmptyProfile( WMT_VER_8_0, ppIWMProfile );

if( FAILED( hr ) )

{

break;

}

}

while( FALSE );

//

// Release all resources

//

SAFE_RELEASE( pIWMProfileManager );

return( hr );

}

HRESULT CWmvWriter::WriteSample(BYTE* pBuffer, DWORD Length)

{

HRESULT hr = S_OK;

if(m_pWMWriter != NULL)

{

INSSBuffer *pSample;

BYTE *pSampleBuff;

HRESULT hr = m_pWMWriter->AllocateSample(Length, &pSample);

if(FAILED(hr))

{

AfxMessageBox("Call AllocateSample Failure!");

return hr;

}

pSample->GetBufferAndLength(&pSampleBuff, &Length);

memcpy(pSampleBuff, pBuffer, Length);

pSample->SetLength(Length);

// Write(pSample);

hr = m_pWMWriter->WriteSample(m_AudioInput, m_MsAudioTime * 10000, 0, pSample);

if(FAILED(hr))

{

AfxMessageBox("Call WriteSample Failure!");

return hr;

}

m_MsAudioTime += MulDiv(Length, 1000, m_MediaType.m_pWFX.nAvgBytesPerSec);

}else AfxMessageBox("Write Sample Fail!");

return hr;

}

类的实现有了,那现在实现button的功能吧,为了方便描述,我这里只是把CD的Track1转为wma。

BYTE Data[CB_AUDIO*NSECTORS];

int StartSector, EndSector, Sectors2Read;

StartSector = cdInfo->GetStartSector(1); // cdInfo是一个CCDAudioInput类指针

EndSector = cdInfo->GetEndSector(1);

m_Progress.SetRange(0, EndSector - StartSector); // m_Progress是进度条

strcat(MusicDir, "Track1.wma"); // MusicDir是输出文件

HRESULT hr = S_OK;

hr = CoInitialize( NULL ); // 记得调用初始化

CWmvWriter m_Writer;

IWMProfile *pProfile;

m_Writer.CreateEmptyProfile(&pProfile);

m_Writer.Initial("", MusicDir, pProfile);

m_Writer.m_pWMWriter->BeginWriting();

for(int sector=StartSector; sector<EndSector; sector+=NSECTORS)

{

Sectors2Read = ( (sector + NSECTORS) < EndSector )?NSECTORS:(EndSector-sector);

if (cdInfo->ReadSector(sector, Data, Sectors2Read))

{

hr = m_Writer.WriteSample(Data, sizeof(Data));

if(FAILED(hr))

{

AfxMessageBox("Failed to WriteSample.");

break;

}

m_Progress.SetPos(m_Progress.GetPos() + Sectors2Read);

}

}

m_Writer.m_pWMWriter->Flush();

m_Writer.m_pWMWriter->EndWriting();

}

结尾:

为了能编译这个程序,你要安装NT4DDK开发包并包含里面的头文件,主要用于对CD音轨的操作,然后还要加入windows media SDK的头文件以及kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib msvcrtd.lib wmvcore.lib winmm.lib odbc32.lib odbccp32.lib 库文件。

好了,今天就到此为止,有什么question,please mailto: ss_liao@163.com, MSN: ss_liao@163.com。大家一起study。Thanks!!!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有