2016年11月9日水曜日

AVIファイルにキャプチャした音声のMP3フォーマットストリームを入れる設定方法

ここ数日、ずっと悩んでました。
未圧縮のPCMならAVIファイルに時間ピッタリで入れ込むことが簡単にできるんですよ。
これがMP3になると、ネタ的に古すぎるのか(0年台初期の話ですからねぇ)
ググっても出てこない、出てきても、どうやるの?って質問ばっかり。

やっと、論理的に整合のつくAPI引数設定がわかりました。
1152がキモな数値。
AVICreateStream()の引数のAVISTREAMINFOの設定を以下のソースコードのように
します。

あとは、waveInOpen()でマイクを設定して、バッファに溜め込まれたPCMデータを
定期的にacmStreamConvert()でMP3データに変換して
AVIStreamWrite()で流し込んでやればいいだけ、のはず。(まだ確認してない)

ただし、Lameのコーデックだと、デフォルトでビットレート可変になってしまっているのか
失敗します。
Lameコーデックのエンコード設定をするコードを書けばいいんでしょうけど、
そこまで調べるのが面倒。。。
ということで、OSに付属のFraunhoferのコーデックを使うことになるのですが、
MSも意地悪な設定をしていて、レジストリを書き換えないとエンコードも可能な
MP3コーデックを使用できないみたい。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32
のmsacm.l3acmの値をl3codecp.acmに変更することで、エンコードも可能なコーデック
'MPEG Layer-3'を使用できるようになります。

これで、キャプチャした動画/音声をx264/MP3のAVIファイルとして作成できるようになった!
(と思う^^;;;)


まだ、STREAMINFOの設定がこなれていないんだよね。
PCMでAUDIO_CAPTURE_BITSPERSAMPLE=24のときどうなるかではっきりすると思う。

#define VIDEO_CODEC 0x34363278 // 'x''2''6''4'
#define VIDEO_CAPTURE_FPS 15
#define VIDEO_CAPTURE_WIDTH 640
#define VIDEO_CAPTURE_HEIGHT 480
#define VIDEO_CAPTURE_BITSPERSAMPLE 32

#define AUDIO_CAPTURE_SAMPLINGFREQUENCY 32000
#define AUDIO_CAPTURE_CHANNELS 2
#define AUDIO_CAPTURE_BITSPERSAMPLE 16

#define MP3_BITRATE  128
#define MP3_SAMPLESPERFRAME 1152
#define MP3_FRAMESIZE (MP3_SAMPLESPERFRAME*((MP3_BITRATE*1000)/8)/AUDIO_CAPTURE_SAMPLINGFREQUENCY)

  PAVIFILE pavifile;
  PAVISTREAM pvid, paud;
  
  HACMDRIVERID hmp3driverid=NULL;

  AVIFileOpen(pavifile, filename, OF_WRITE|OF_CREATE, NULL);

  BITMAPINFOHEADER bi;
  memset(&bi, 0, sizeof(BITMAPINFOHEADER));
  bi.biSize=sizeof(BITMAPINFOHEADER);
  bi.biWidth=VIDEO_CAPTURE_WIDTH;
  bi.biHeight=VIDEO_CAPTURE_HEIGHT;
  bi.biPlanes=1;
  bi.biBitCount=VIDEO_CAPTURE_BITSPERSAMPLE;
  bi.biCompression=BI_RGB;
  bi.biSizeImage=bi.biHeight*bi.biWidth*(bi.biBitCount/8);

  RECT rc;
  SetRect(0, 0, VIDEO_CAPTURE_WIDTH, VIDEO_CAPTURE_HEIGHT);

  WAVEFORMATEX wfx;
  memset(&wfx, 0, sizeof(WAVEFORMATEX));
  wfx.wFormatTag=WAVE_FORMAT_PCM;
  wfx.nChannels=AUDIO_CAPTURE_CHANNELS;
  wfx.nSamplesPerSec=AUDIO_CAPTURE_SAMPLINGFREQUENCY;
  
  wfx.wBitsPerSample=AUDIO_CAPTURE_BITSPERSAMPLE
  wfx.nBlockAlign=wfx.wBitsPerSample/8*wfx.nChannels;
  wfx.nAvgBytesPerSec=wfx.nSamplesPerSec*wfx.nBlockAlign;
  
  MPEGLAYER3WAVEFORMAT mp3wf;
  memset(&mp3wf, 0, sizeof(MPEGLAYER3WAVEFORMAT));
  mp3wf.wfx.wFormatTag=WAVE_FORMAT_MPEGLAYER3;
  mp3wf.wfx.nChannels=AUDIO_CAPTURE_CHANNELS;
  mp3wf.wfx.nSamplesPerSec=AUDIO_CAPTURE_SAMPLINGFREQUENCY;
  
  mp3wf.wfx.wBitsPerSample=0;
  mp3wf.wfx.nBlockAlign=1;
  mp3wf.wfx.nAvgBytesPerSec=(MP3_BITRATE*1000)/8;
  
  mp3wf.wfx.cbSize=MPEGLAYER3_WFX_EXTRA_BYTES;
  mp3wf.wID=MPEGLAYER3_ID_MPEG;
  mp3wf.fdwFlags=MPEGLAYER3_FLAG_PADDING_OFF;
  mp3wf.nFramesPerBlock=1;
  mp3wf.nBlockSize=MP3_FRAMESIZE*mp3wf.nFramesPerBlock;
  mp3wf.nCodecDelay=1393;

  // video stream
  AVISTREAMINFO si;
  memset(&si, 0, sizeof(AVISTREAMINFO));
  si.fccType=streamtypeVIDEO;

  si.dwScale=1;
  si.dwRate=VIDEO_CAPTURE_FPS;
  si.fccHandler=VIDEO_CAPTURE_CODEC;
  si.rcFrame=rc;
  AVIFileCreateStream(pavifile, &pvid, &si);

  AVICOMPRESSOPTIONS op;
  memset(&op, 0, sizeof(AVICOMPRESSOPTIONS));
  op.fccType=streamtypeVIDEO;
  op.fccHandler=VIDEO_CAPTURE_CODEC;

  PAVISTREAM pvidcomp;
  AVIMakeCompressedStream(&pvidcomp, pvid, &op, NULL);
  AVIStreamRelease(pvid);
  pvid=pvidcomp;

  AVIStreamSetFormat(pvid, 0, &bi, sizeof(BITMAPINFOHEADER));

  // audio stream
  AVISRETAMINFO si;
  memset(&si, 0, sizeof(AVISTREAMINFO));
  si.fccType=streamtypeAUDIO;

  if(hmp3driverid==NULL){
    // wav
    si.dwSampleSize=AUDIO_CAPTURE_BITSPERSAMPLE/8*AUDIO_CAPTURE_CHANNELS;
    si.dwScale=si.dwSampleSize;
    si.dwRate=AUDIO_CAPTURE_SAMPLINGFREQUENCY*si.dwSampleSize;
    AVIFileCreateStream(pavifile, &paud, &si);
    AVIStreamSetFormat(paud, 0, &wfx, sizeof(WAVEFORMATEX));
  }else{
    // mp3
    si.dwSampleSize=MP3_FRAMESIZE;
    si.dwScale=MP3_SAMPLESPERFRAME;
    si.dwRate=AUDIO_CAPTURE_SAMPLINGFREQUENCY*si.dwSampleSize;
    AVIFileCreateStream(pavifile, &paud, &si);
    AVIStreamSetFormat(paud, 0, (LPWAVEFORMATEX)&mp3wf, sizeof(MPEGLAYER3WAVEFORMAT));
  } 


0 件のコメント:

コメントを投稿