作成者別アーカイブ: はごろも いかお

はごろも いかお について

・画像のピクセルフォーマットや画素のバイナリデータ加工 ・組み込み系Linux ・router なんかの開発をC/C++でやっているジジイです 「六本木は萌えているか」 萌えます萌えます

今度はノコギリ波をつくろう

毎度、いかおです。

前回はサイン波を作成してならせてみせましたが今度はノコギリ波を作ります。

先ず、ノコギリ波はどんな波形かというと・・・・

そこはグーグル先生に聞いて下さい。

【その原理】

「キャリアとモジュレータとの周波数を1:1にするとノコギリ波が作れる」

ということなので、いわゆるFM音源的アプローチで(サイン波はできてるので)作ってみます

先ず、1サイクルのサイン波を作成する。コレは前回の私の投稿であるここを参考にして・・・・

例えば1サイクルのサイン波はこんな感じで作れるかな?

static const float PI = 3.141592f;
static const float SamplingRate = 48000.0f;
static const float Heltz = 440.0f; //ラ〜♪
static const float DegBase = 360.0f;
float width = DegBase / (SamplingRate / Heltz);

float vals[360];
floart deg = 0.0f;
int n = n;
for(int i=0;i < 360){
    if(deg > DegBase)break;
    vals[i] = sin(deg * PI / 180);
    deg = deg + width;
   n++;
}

そしてアドレステーブルを作成する。FM音源はこのアドレステーブルを他のサイン波と加算したりして変調し変調された順番で元のサイン波を取り出す。なんだか無茶な方法に見えるけどしょせん楽器なんでいいか・・・

int addresTable[360];
// アドレステーブルはintにしとこう
for(int i=0;i < n;i++){
    addressTable[i]=i;
}

そして変調だ。元のサイン波と混ぜてしまう

int editedAddressTable[360];
//これもint
for(int i=0;i < n;i++){
    editedAddressTable[i] = vals[i] + addressTable[i];
}

そして取り出す・・・

float answer[360]:
for(int i=0;i < n;i++){
    answer[i] = vals[editedAddressTable[i]];
}
// このanswerがFM変調後の音です

これが本当にノコギリ波になるのか?

次回、作ってみます


周波数440Hzの正弦波をサンプリングレート48Kで作ってみる(実践編)

タグ: , | 投稿日: 投稿者:

毎度いかおです

前回-周波数440Hzの正弦波をサンプリングレート48Kで作ってみる(考察編)-で考えたことを実行してみます。

 

データ的にサイン波になったよ〜パチパチ

 

ではつまらんので実際にWAVを作ります

その前に前回の考察の誤りについて

サンプル取得のステップ幅 = 1回転の長さ / 360

は間違いですね。1つのサンプルでどれだけ角度が変わるかですし、1回転の長さってのも変で、"1回転分のサンプル数"っていうのが正しいですね。

サンプル取得のステップ幅 = 360 / 1回転分のサンプル数

でWAVの作り方はgoogle先生に"wav フォーマット 解析"と聞くと答えて下さります。

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

static const double     PI      =       3.1415926535f;
static const double     Volume  =       2048.0f;
static const double     SampleRate      =       48000.0f;
static const double     Heltz   =       440.0f;
static const double     DegBase =       360.0f;
static const int        SampleSec       =       5;
static double SampleWidth;

typedef struct {
        char            header[4];
        uint32_t        fileLength;
        char            fmtHead[4];
} FileHeader;

typedef struct {
        char            chunkID[4];
        uint32_t        chunkSize;
        uint16_t        formatCode;
        uint16_t        channels;
        uint32_t        sampleRatio;
        uint32_t        sampleBytePerSeccond;
        uint16_t        alignment;
        uint16_t        bitDepth;
} FormatChunk;

typedef struct {
        char    chunkID[4];
        int32_t chunkSize;
} DataChunk;

int main(int argc, char **argv){
        FILE    *fp     =       NULL;
        double  deg     =       0.0f;
        size_t  asize;
        short   *sample =       NULL;
        long    i;
        size_t  sumSize =       0;

        FileHeader      fileHeader;
        FormatChunk     formatChunk;
        DataChunk       dataChunk;

        fileHeader.fileLength = (sizeof(FileHeader) + sizeof(FormatChunk) +
                        sizeof(DataChunk) + sizeof(short) * (int)SampleRate * SampleSec) - 8;
        memcpy(fileHeader.header,       "RIFF", 4);
        memcpy(fileHeader.fmtHead,      "WAVE", 4);

        memcpy(formatChunk.chunkID,     "fmt ", 4);
        formatChunk.chunkSize   =       16;
        formatChunk.formatCode  =       1;
        formatChunk.channels    =       1;
        formatChunk.sampleRatio =       (unsigned long)SampleRate;
        formatChunk.sampleBytePerSeccond        =       (unsigned long)SampleRate * 2;
        formatChunk.alignment           =       2;
        formatChunk.bitDepth    =       16;

        if(argc < 2){
                printf("Argument 1 must be output wav name\n");
                return -2;
        }

        fp = fopen(argv[1], "wb");
        if(fp == NULL){
                printf("file:%s open error\n", argv[1]);
                return -3;
        }
        sample = (short *)malloc(sizeof(short) * SampleRate * SampleSec);
        if(sample == NULL){
                fclose(fp);
                printf("Allocation error(%lu)\n", sizeof(short) * (int)SampleRate * SampleSec);
                return -4;
        }
       // 今回訂正した部分
        SampleWidth = DegBase / (SampleRate / Heltz);
        memcpy(dataChunk.chunkID,       "data", 4);
        dataChunk.chunkSize     =       sizeof(short) * SampleRate * SampleSec;
        for(i=0;i < SampleRate * SampleSec;i++){
    // 結果が-Volume から Volume までの値のためVolume分プラスに寄せます
                sample[i] = (short)(round(Volume * sin(deg * PI / 180)) + Volume);
                deg     =       deg + SampleWidth;
        }
        fwrite((char *)&fileHeader, 1, sizeof(FileHeader), fp);
        fwrite((char *)&formatChunk, 1, sizeof(FormatChunk), fp);
        fwrite((char *)&dataChunk, 1, sizeof(DataChunk), fp);
        fwrite((char *)sample, 1, sizeof(short) * (int)SampleRate * SampleSec, fp);
        free(sample);
        fclose(fp);
        return  0;
}

それから、ボリュームですが128では小さい(これでも小さいが・・)ので2048にしてます。

できたのが、これです

おお、ラの音じゃん。


周波数440Hzの正弦波をサンプリングレート48Kで作ってみる(考察編)

タグ: | 投稿日: 投稿者:

まいど、いかおです

表題の件、考えてみます。間違ってたらツッコミをよろしくお願いします。

サイン波ですからsin/cos/tanのsinを使いますよね。これを1秒間に440回転させればいいんです。

ここでサンプリングレートが48Kですから、1回転の長さ(サンプル数)は

1回転の長さ=48000/440

この長さで1回転させるのでサンプル取得のステップ幅は

サンプル取得のステップ幅 = 1回転の長さ / 360

で、コード的にはこんな感じなのか?

// 1秒分の440Hzのサイン波作成
// サンプルはunsigned shortで作ってみる(16bit)
// 音量はどうしよう・・・とりあえず128
static const double PI =  3.1415926535;
static const short vol = 128;
double sampleWidth = (48000 / 440) / 360;
double deg = 0.0f;

unsigned short sample[48000]:

for(int i=0;i < 48000;i++){
    sample[i] = (unsigned short)vol * sin(deg * PI /180);
    deg = deg + (48000 / 440) / 360;
}

次回、実際にやってみて検証します。


C言語でCGIしてみましょう!!

タグ: | 投稿日: 投稿者:

まいど。いかおです

今やraspberry piなんかでperlが使えるご時世にあえてC言語でCGIの例を・・・

・ヘッダーの情報はgetenv()で取得できる

・FORMの情報はPOSTならstdinからCONTENT_LENGTH分読み込んだもの、GETならヘッダ情報のQUERY_STRINGの内容である

・responceは基本的にstdoutへ書き出す

ヘッダ情報の取り出し例

/* Enviroment element */
const char      *c_list[] = {"SERVER_SOFTWARE", "SERVER_NAME", "GATEWAY_INTERFACE", "SERVER_PROTOCOL",
                        "SERVER_PORT", "REQUEST_METHOD", "PATH_INFO", "PATH_TARNSRATED", "SCRIPT_NAME",
                        "QUERY_STRING", "REMOTE_HOST", "REMOTE_ADDR", "AUTH_TYPE", "REMOTE_USER",
                        "REMOTE_IDENT", "CONTENT_TYPE", "CONTENT_LENGTH", "HTTP_ACCEPT", "HTTP_USER_AGENT", ""};

 for (i=0;c_list[i][0] != 0x00;i++) {
    if ((p=getenv (c_list[i])) != NULL) {
         p; // これをなんとかする
    }
}

FORM情報の取得(POST)

#define MAX_PARALEN 4096
char    buff[MAX_PARALEN]

memset (buff, 0x00, MAX_PARALEN);
int pos=0;
int remain=length;
while (remain > 0) {
    if ((len=fread (&(buff[pos]), 1, remain, stdin)) > 0) {
        remain = remain - len;
        pos = pos + len;
    }
}

ここで取得したbuffを'&'でセパレートしてやれば

name=valueが取り出せます

pos=0; pcnt=0;
for (i=0;i < length ;i++) {
    if (buff[i]=='&') {
       // ここがname=valueの先頭アドレス
        &(buff[pos]);
        // ここがname=valueの長さ
        (i - pos));
    }
}

responceの例

printf()とかでstdoutに吐き出していきます

static  const   char    *index_html[] = {"&lt;!DOCTYPE html&gt;\n",
                                                           "&lt;html&gt;\n",
                                                           "&lt;body&gt;\n",
                                                           "&lt;h1&gt;Hello world&lt;/h1&gt;\n",
                                                           "&lt;/body&gt;\n",
                                                           "&lt;/html&gt;\n",
                                                           ""
};
.
.
for(int i=0;index_html[i] != "";i++){
    printf(index_html[i]);
}
.
.

※本気でやりたい人がいますかね。。。。


SAR(PAR) DAR ってなんですか?

タグ: , , , | 投稿日: 投稿者:

久々のいかおです

ffmpeg を使っているとぶち当たります(いや、それ以外でも)

私はいつも、えっと、なんだっけ?ってなるので書いておきたいと思いました

sar(par)が1:1の場合は良いのです。ここが妙ちくりんなことになってくると登場人物が不要に痩せたり太ったりします。

SARがSample Aspect Ratioで、PARがPixel Aspect Ratioです

DARはDisplay Aspect Ratio・・・・・

「そんなこたぁ聞いてねぇんだよ」

と言われそう。ここぢゃないんです

ちなみに、SARとPARは同じことです。

で、いっつも悩ましいのは

SARに変な値が入っているとDARもパルプンテ(実は違います)なんですね。だからスッキリするために

計算式をメモるわけです。

SARはpixelの形を表します。ピクセルの横幅と縦幅の比です

DARは出来上がった画像の縦横比になります。

だから、元々のピクセル解像度にそれぞれSARの比率をかけるとDARになるってことです。つまり

元のpixel解像度が

720×480

で、SARが8:9なら

720 x 8 : 480 x 9 = 4 : 3

で、DARが 4:3 となる訳です

じゃあね、

720 x 540 の画像を 16:9 で出したい場合SARはどうなるんですか?

こうするんですよ(私もそのまま覚えこむことにしました)

元画像の高さ x 出したい横幅 : 元画像の幅 x 出したい縦幅比

540 x 16 : 720 x 9 = 8640 : 6480 =  4 : 3 (SARね)

・・・おおおお

720 x 480(ntsc)を16:9で

480 x 16 : 720 x 9 = 32 : 27

おお・・・なんだか呪文っぽくなってきたぞ

そうそう、パルプンテだったやつは

pixelが720×480でSARが 40:33 でDARが 20:11ですってよ!

そうか、そうかDARにするためのSARだから

480 x 20 : 720 x 11 = 40:33

ああ、そういうことだったのか。

私はこの理解に苦しんだんだけど、同じつまづきした人っていないの?


音声の倍速再生

タグ: , | 投稿日: 投稿者:

毎度、いかおです

前回の続きで倍速再生について、特に音声の切り詰めかたを考えてみた。

・音声をある一定の長さできる

・それをつなげる

・のりしろを作る

まずは、これを図にしてみました

baisoku

1番上が元の波形(だと思って!)で、矢印がついてる部分がサンプル対象です。

2番めはサンプル対象をくっつけた図。オレンジの線はその間のサンプル

3番めはのりしろをフェードアウト/フェードインしたところです。ここで赤い線の様になってショックノイズを低減させます

フェードイン/アウト コード例】

#define NORISHIRO 100 // 例えばのりしろは100個
.
.
.
// 音声デコードがされたら
avcodec_decode_audio4(audioCodecCtx, decodedAudio, &amp;amp;amp;decodedSize, &amp;amp;amp;packet);
if (decodedSize > 0){
    size_t packetSize;
    // flgならsample出す
    if(flg){
        // m_sampleCount のりしろ長さ
        packetSize = decodedAudio->nb_samples * sizeof(short) * CHANNEL;
        short *samp = (short *)decodedAudio->data[0];
        flg = false;
        // サンプルの中に左右交互にデータがある(プラナーでない)
        for(int i=0;i < m_sampleCount;i++){
            samp[(i * 2)] = (short)(((float)m_bef[(i * 2)] * (((float)m_sampleCount - (float)i - 1.0f) / (float)m_sampleCount)) +
            ((float)samp[(i * 2)] * (((float)i + 1 ) / (float)m_sampleCount)));
            samp[(i * 2) + 1] = (short)(((float)m_bef[(i * 2) + 1] * (((float)m_sampleCount - (float)i - 1.0f) / (float)m_sampleCount)) +
           ((float)samp[(i * 2) + 1] * (((float)i + 1 ) / (float)m_sampleCount)));
        }
        // ここが再生データ
        decodedAudio->data[0];
        // これが再生データの長さ
        packetSize;
    } else {
        // ! flgならsample保存(フェードアウトに利用)
        if(decodedAudio->nb_samples > NORISHIRO){
            m_sampleCount = NORISHIRO;
        } else {
            m_sampleCount = decodedAudio->nb_samples;
        }
        memcpy((char *)m_bef, (char *)decodedAudio-&gt;data[0], m_sampleCount * sizeof(short) * 2);
        flg = true;
    }
}
.
.
.

ひとまずはこれでさほどノイズが気にならなくなりました。

1度に採れるサンプル数にもよりますが、のりしろは長くとった方がキレイに再生できます


動画の倍速再生の考え方

タグ: , | 投稿日: 投稿者:

こんにちは毎度お騒がせします、いかおです

早送りと遅送り(?) まず、映像だけ考えていきますと・・・・・

1.再生時にフレームレートを偽る

→毎秒30フレームの動画であれば、これを毎秒60フレームの動画として再生すれば2倍速、毎秒15フレームで再生すれば0.5倍速になる。

2.フレームを間引いたり、水増しする

→再生対象フレームを半分にすると2倍速、同じフレームを2回づつ再生すると0.5倍速になる。

1の方法は昔のビデオテープの早送りに似た方法ですね。 デバイスその他が対応すれば、サンプリングレートを自在に操って様々な再生に対応できます。 遅くする方は簡単なのですが、早くする方は場合によって、デバイスが対応しないことがありそうです。

多分、普通は2の方法を採用しますね。ただ、これも2倍、3倍、1/2倍、1/3倍位はたやすいのですが、 1.5倍とか2/3倍とかの割り振りというか、計算は大変そうです。 この様に映像の方は案外簡単に速度変更できます。ま、こういうところは所詮ぱらぱら漫画の延長なので、考え方的にも 楽ですね。

でも、音を考えると意外に難しいのがわかります。

ここで、色んな難しいパターンを排除して、音声の2倍速再生を想像して下さい

【音の2倍速再生をどうするか?】

皆さんならどうします?

映像と同じで音声サンプルを半分間引くと、半分の長さの同じ様な波形ができます。

これは実際、間引いたためのギャップでノイズが多発するのと、そうです、

周波数があがるのです。つまり、音が高くなります。

昔のアナログビデオやカセットテープ(みんな知らないか?)の早送りみたいです。

映像でいくところのフレームレートの偽りと似ています。そんなことなら

最初からサンプリングレートを偽って再生すりゃいいんですよね。

でも、現実的じゃない。じゃ、間引きでいきますか?

「いや、それでいいよね。普通」

っていうなよ絶対。ダメ絶対

音程を上げなくても倍速再生できるんです。

音は実はいろいろ奥が深くて様々な方法があるのですが、概ね

こんな方法が採られます。

・音をある時間的長さのかたまりに分割して考える

・その大きさで間引いて、それぞれをつなぎ合わせる

・つなぐ部分にのりしろを設けて、のりしろ部分で前のかたまりのフェードアウトと次のかたまりのフェードインを合成する

#これはノイズ除去のために行います

実際のコードは次回必ず載せます。

それでは


V4l2でアプリケーション作りました

カテゴリー: 映像 | 投稿日: | 投稿者:

ひっさびさの いかお です。

前回の宣言通りつくってみました。

MJPEGで撮影できるUVCカメラで動画撮影し、とれた映像をそのまま書いたら
JPEGじゃね?

と思って作ってみますた

カメラはlogicoolのC920Tです。
多少不安定な感じだけど、あっさり認識した

[6734110.675010] usbhid 3-2:1.0: >couldn't find an input interrupt endpoint
[6734110.675925] usbcore: registered new interface driver usbhid
[6734110.675927] usbhid: USB HID core driver
[6734966.300322] usb 3-1: >new high-speed USB device number 11 using xhci_hcd
[6734966.319401] usb 3-1: >New USB device found, idVendor=046d, idProduct=082d
[6734966.319405] usb 3-1: >New USB device strings: Mfr=0, Product=2, SerialNumber=1
[6734966.319408] usb 3-1: >Product: HD Pro Webcam C920
[6734966.319410] usb 3-1: >SerialNumber: A0313D6F
[6734966.319925] uvcvideo: Found UVC 1.00 device HD Pro Webcam C920 (046d:082d)
[6734966.320342] input: HD Pro Webcam C920 as /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/input/input37
[6734968.751553] xhci_hcd 0000:00:14.0: >WARN Event TRB for slot 10 ep 0 with no TDs queued?

中核部分はこんな感じ

////////////////////////////////////
// O P E N   D e v i c e          //)
////////////////////////////////////
        camFd   =       open(devName, O_RDWR);
        if(camFd == -1){
                fprintf(stderr, "Device(%s) open failed\n", devName);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto threadExit;
        }
        // setup resolution etc
        memset((void *)&fmt, 0x00, sizeof(fmt));
        fmt.type                =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width       =       startParm->width;
        fmt.fmt.pix.height      =       startParm->height;
        fmt.fmt.pix.pixelformat =       V4L2_PIX_FMT_MJPEG;
        ret = xioctl(camFd, VIDIOC_S_FMT, &fmt);
        if(ret < 0){
                fprintf(stderr, "Settup failed\n");
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        if(fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG){
                fprintf(stderr, "WARNING!! Assigned incorrect fmt:%d resolition:%dx%d\n",
                        (int)fmt.fmt.pix.pixelformat,
                        (int)fmt.fmt.pix.width,
                        (int)fmt.fmt.pix.height);
        }
        ///////////////////////////////////
        // memory mapping
        ///////////////////////////////////
        // request mmap
        memset((void*)&req, 0x00, sizeof(req));
        req.count       =       MMAP_CNT;
        req.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory      =       V4L2_MEMORY_MMAP;
        ret = ioctl(camFd, VIDIOC_REQBUFS, &req);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_REQBUFS failed\n");
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        if(req.count != MMAP_CNT){
                fprintf(stderr, "mmap allocate failed(%d)\n", req.count);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        // map to userland)
        for(i=0;i < MMAP_CNT;i++){
                memset((void*)&buf, 0x00, sizeof(buf));
                buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      =       V4L2_MEMORY_MMAP;
                buf.index       =       i;
                ret = xioctl(camFd, VIDIOC_QUERYBUF, &buf);
                if(ret < 0){
                        fprintf(stderr, "mmap VIDIOC_QUERYBUF[%d]\n", i);
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        goto closeExit;
                }
                mmap_p[i] = mmap(NULL, buf.length, PROT_READ, MAP_SHARED, camFd, buf.m.offset);
                if(mmap_p[i] == MAP_FAILED){
                        fprintf(stderr, "mmap(%d)\n", errno);
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        gExecuted       =       0;
                        goto closeExit;
                }
                mmap_l[i]       =       buf.length;
#if 1
                fprintf(stderr, "mmbuff[%d] length is \"%u\"\n", i, buf.length);
#endif
        }
        // Queueing before
        for(i=0;i < MMAP_CNT;i++){
                memset((void*)&buf, 0x00, sizeof(buf));
                buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      =       V4L2_MEMORY_MMAP;
                buf.index       =       i;
                ret = xioctl(camFd, VIDIOC_QBUF, &buf);
                if(ret < 0){
                        fprintf(stderr, "VIDIOC_QBUF error\n");
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        goto closeExit;
                }
        }
        ////
        // start stream
        ////
        type    =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = xioctl(camFd, VIDIOC_STREAMON, &type);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_STREAMON error:%d\n", ret * -1);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        // Capture Stanby
        startParm->stanby       =       1;
        //
        while(gExecuted){
                fd_set  fds;
                FD_ZERO(&fds);
                FD_SET(camFd, &fds);
                struct  timeval timeOut;
                timeOut.tv_sec  =       0;
                timeOut.tv_usec =       USEC_1MSEC;
                ret = select(camFd + 1, &fds, NULL, NULL, &timeOut);
                if(ret < 0){
                        if(errno == EINTR)
                                continue;
                        fprintf(stderr, "select() error\n");
                        *exitStatus     =       (int)FWCAMCAPT_OTHER;
                        break;
                }
                if(FD_ISSET(camFd, &fds)){
                        memset(&buf, 0, sizeof(buf));
                        buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                        buf.memory      =       V4L2_MEMORY_MMAP;
                        ret = xioctl(camFd, VIDIOC_DQBUF, &buf);
                        if(ret < 0){
                                fprintf(stderr, "VIDIOC_DQBUFi error\n");
                                *exitStatus     =       (int)FWCAMCAPT_OTHER;
                                break;
                        }
                        if(pthread_mutex_lock(&(startParm->bufMutex)) == 0){
                                memcpy(startParm->frameBuff, mmap_p[buf.index], buf.bytes[6734110.675010] usbhid 3-2:1.0: >couldn't find an input interrupt endpoint
used);
                                startParm->length       =       buf.bytesused;
                                pthread_mutex_unlock(&(startParm->bufMutex));
                        }
                        ret = xioctl(camFd, VIDIOC_QBUF, &buf);
                        if(ret < 0){
                                fprintf(stderr, "VIDIOC_QBUF error\n");
                                *exitStatus     =       (int)FWCAMCAPT_OTHER;
                                break;
                        }
                }
#if 0
                else
                        fprintf(stderr, "TO\n");
#endif
        }
        ////
        // stop stream
        ////
        type    =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = xioctl(camFd, VIDIOC_STREAMOFF, &type);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_STREAMOFF error:%d\n", ret * -1);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
////////////////////////////////////
// C L O S E   D e v i c e        //
////////////////////////////////////
closeExit:
        close(camFd);

これを説明していくと、基本的にこの中核処理はサブスレッドで動いていて、
カメラからMJPEGのフレームが来たら
startParm->frameBuff
にコピーしてるってわけです。

ここを適当なタイミング(1秒毎とか)覗いてフレームが
あったら(ここには書いてないけど)xxx.jpegという名前で書いてます
※ここでの書き込みと、フレームデータの参照が重ならないように
mutex()してます

それと、コードの中で
camFd = open(devName, O_RDWR);
ってのがあります。
最初、カメラからビデオを読むだけだからO_RDONLYで良いと浅はかに考えていたけど、
よく考えてみたらカメラデバイスをコントロールするため、ioctl()で制御してるんだから
書き込みがいるんだよね。

ちなみに
xioctl()
アットマークテクノさんのページ
を参考にしました(全体的にも参考にしてます)

内容的には以下のコード

//////////////////////////////////////////////////////////
// xioctl copy from 
// http://manual.atmark-techno.com/armadillo-810/armadillo-810_product_manual_ja-1.3.0/ch14.html
/////////////////////////////////////////////////////////
static int xioctl(int fd, int request, void *arg){
int     ret;
        for(; ; ){
                ret = ioctl(fd, request, arg);
                if(ret < 0){
                        if(errno == EINTR)
                                continue;
                        return -errno;
                }
                break;
        }
        return 0;
}

メモリマップとユーザーランドのやりとりの低レベル
(っていうかioctl()とかしてる段階で感じるけど)な感じが
オタク心をくすぐるか?


Video For Linux(v4l)について

タグ: , | 投稿日: 投稿者:

Webカメラなどから撮影した動画を取り込んで××するんならWindowsなら今時はMedia Foundationでしょうか?いまだにDirectShowが便利なんでしょうか?
以前にDirectShowでフィルターを書いて色々実装しましたが、フィルターグラフの接続とかが面倒だったりしました。最近の私の場合はLinux(Ubuntu)なので、DirectShowのお世話になっていません。

以前Linuxでカメラでキャプチャした画像を××するのに実際に私が行ったのは、BlackMagicDesign社のビデオボードDecklink/Intensityシリーズにカメラを接続する形で行いました。BlackMagic Decklink/IntensityにはBlackmagic DeckLink SDKを利用してアクセスし、画像の生データを得ました。

BlackMagic Decklink/Intensityにカメラをさしても/dev/videoXが生えてくるわけじゃないので使えませんでしたが、今度はV4lです

※実際にはOpenCvでキャプチャ処理とかも書きましたが、これはOpenCv内部からv4lを呼んでるんですね

1.オープン

 int open(const char *pathname, int flags);
 で行う。pathnameに"/dev/video0"とか、flagsにはO_RDONLY, O_RDWR
 とか設定

2.カメラ操作
※カメラで操作可能な設定をいじったり、バッファを関連付けたり、画素データを取り込んだりします

int ioctl(int fd, int request, ...);
で行う。リクエスト(request)については下に例示

リクエストの例

VIDIOC_QUERYCAP カメラデバイスに保有する機能を問い合わせます
VIDIOC_S_FMT カメラデバイスに画像データのフォーマットを設定
VIDIOC_G_FMT カメラデバイスに画像データのフォーマットを取得
VIDIOC_REQBUFS 画像データ用のバッファをカメラデバイスに要求
VIDIOC_QUERYBUF VIDIOC_REQBUFSで要求したバッファの情報を取得
VIDIOC_QBUF バッファをエンキューします
VIDIOC_DQBUF バッファをデキューします
VIDIOC_STREAMON キャプチャ開始
VIDIOC_STREAMOFF キャプチャ停止

3.select()で待つ(ってことはpoll()も使えんのか)ことで、キューにデータ入ったか確認する
4.(カメラの)バッファをユーザープログラムにマッピングする

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
で行う

※ユーザー定義のポインタにバッファアドレスをマッピングする

★で、手順的には

  1. カメラデバイスオープン
  2. 設定(解像度など)
  3. バッファ要求
  4. バッファにポインタ割り当て
  5. バッファをキュー(あらかじめ)
  6. キャプチャ開始
  7. デキュー/エンキュー繰り返し→フレームデータの処理
  8. キャプチャ終了
  9. 後始末

となります。

以上ですが、今度は、ちゃんと何かつくりますね。これとffmpegのlibで何か作るとか、OpenCvと組み合わせるとかで・・・・


ffmpegの音声フォーマットがFLTPですって!?

タグ: | 投稿日: 投稿者:

私が自分のアプリケーションに利用していたffmpegのライブラリなんですが、そろそろ最新のffmpegに入れ替えようとしました。
すると、今まで符号付き16ビットとして利用出来ていた動画の音声パケットフォーマット(s16xx)が、FLTP(?)とかになっていました

このタイプをチェックしていたためアプリはパケットを処理せず終了していました。

そこで、

「そんなチェックハズしゃええやん」

とチェックを単に外すと音パケットが全然足りなくなりました。google先生に尋ねるとフォーラムとかで私と同じ境遇の人々がいて質問していました。
その回答で英語で

You need to remember that AV_SAMPLE_FMT_FLTP is a planar mode. If your code is expecting an AV_SAMPLE_FMT_S16 (interleaved mode) output, you need to reorder the samples after converting. Considering 2 audio channels and using interleaved mode, the samples are ordered as “c0, c1, c0, c1, c0, c1, …”. Planar mode is “c0, c0, c0, …, c1, c1, c1, …”.

プラナーって映像とプラナーしてるわけ?
だとしたらどうしたら良い?
と思っていたら。どうやらこれは(例えばステレオなら)
ch0(R)0,1,2,3,4,5,6,7,8…….
ch1(L)0,1,2,3,4,5,6,7,8…….
と音声チェネルがプラナーしてるってことが分かった。そしてさらに、
音声の値もフローティングポイント、すなわちfloat型で値の範囲が-1.0~1.0であることが分かった

フロートの値は符号付き16ビットに変換するのでそれぞれ32768を乗算してやれば、
-32768~32768
になる。そして、
右の1個めを最初に、左の1個めを次に、右の2個めをその次、左の2個めをその次・・・・・・・
とやって元の16bitのフォーマットが取り返せる

で、そのプラナーな要素は以下に格納されている
デコード結果(AVFrame)のextended_data[n] //チャネルnの音声パケット
ということで以下にコード例(ここではステレオ限定で)

//デコードが終わってから
avcodec_decode_audio4(audioCodecCtx, decodedAudio, &decodedSize, &packet);
if(audioCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP){
        short *audioPkt   = NULL;					// パケット格納域
        size_t packetSize = 0;						// パケットバイト長
        int    sampleCnt  = audioCodecCtx->decodedAudio->nb_samples;	// サンプル数
        float *channel0   = (float*)decodedAudio->extended_data[0]; // 元の型はuint8_tな
        float *channel1   = (float*)decodedAudio->extended_data[1];
        packetSize        = sampleCnt * 2 * sizeof(short);    // バイトサイズ(ステレオ2ch*sizeof(short)*サンプル数)
        if(packetSize){
            audioPkt = (short *)av_mallocz(packetSize);       // ffmpegなんで・・・
            for(int i=0;i < sampleCnt;i++){       // サンプル数分
                audioPkt[i*2]   = (short)(channel0[i] * 32767.0f);
                audioPkt[i*2+1] = (short)(channel1[i] * 32767.0f);
            }
        }
        // この'audioPkt'が今までの16ビットデータなので。これを使う
} else {
..................
}

※エラーチェックとか、領域の開放とかが抜けてますからね。このままコピペはダメですよ

これ、最初
audioPkt[i*2] = (short)(channel0[i] * 32767.0f);
のところを
audioPkt[i*2] = (short)channel0[i] * 32767.0f;
としてたら全然音が出なかった。
良い子の皆さんなら何故だかわかるよね(私は馬鹿でした)

なぜffmpegがこの様に変更したかとかは知りませんが、以下参考までに
ffmpegを新しくするのに私が遭遇したのは、その他では
・フレームのアロケーションで
avcodec_alloc_frame()を使わないでav_frame_alloc()を使う
・フレームの初期値設定
avcodec_get_frame_defaults()を使わないでav_frame_unref()を使う
・AVCODEC_MAX_AUDIO_FRAME_SIZEの定義が無くなった
→1秒分を想定した長さを入れとけってことで別名で(ここでは)192000を定義しました
以上で動くには動きました。