2

答え

https://stackoverflow.com/a/12507520/962890

それはとても些細なことでした..議論!しかし、多くの良い情報が得られました。みんなありがとう。

編集

github へのリンク: https://github.com/MarkusPfundstein/stream_lame_testing

元の投稿

パイプラインを介した IPC についていくつか質問があります。私の目標は、TCP/IP ストリームごとに MP3 データを受信し、それを LAME にパイプして wav にデコードし、計算を行ってディスクに (wav として) 保存することです。全体にノンブロッキング IO を使用しています。少しイライラするのは、tcp/ip の読み取りがパイプラインのトラフよりもはるかに高速であることです。〜3 MBのmp3を送信すると、ファイルはクライアント側で数秒で読み取られます。最初に、ラメプロセスのstdinに書き込むこともできます。書き込みを停止し、残りのmp3を読み取り、終了したらラメに再度書き込むことができます。4096 バイトは約 1 秒かかります (ラメからの書き込みと読み取りに)。wav min 128kbs をデコードしたいので、これはかなり遅いです。

OS は、このマイクロ コンピューター上の debian 2.6 カーネルです。

https://www.olimex.com/dev/imx233-olinuxino-maxi.html

65MB RAM 400MhZ

ulimit -n | grep パイプは 512 x 8 を返します。これは 4096 を意味し、これで問題ありません。その32ビットシステム。

奇妙なことは、

my_process | lame --decode --mp3input - output.wav

非常に速く進みます。

これが私のfork_lameコードです(これは本質的に私のプロセスのスタウトをラメの標準入力に接続し、その逆も同様です)

static char * const k_lame_args[] = {
  "--decode",
  "--mp3input",
  "-",
  "-",
  NULL
};

static int
fork_lame()
{
  int outfd[2];
  int infd[2];
  int npid;
  pipe(outfd); /* Where the parent is going to write to */
  pipe(infd); /* From where parent is going to read */
  npid = fork();
  if (npid == 0) {
    close(STDOUT_FILENO);
    close(STDIN_FILENO);
    dup2(outfd[0], STDIN_FILENO);
    dup2(infd[1], STDOUT_FILENO);
    close(outfd[0]); /* Not required for the child */
    close(outfd[1]);
    close(infd[0]);
    close(infd[1]);
    if (execv("/usr/local/bin/lame", k_lame_args) == -1) {
      perror("execv");
      return 1;
    }
  } else {
    s_lame_pid = npid;
    close(outfd[0]); /* These are being used by the child */
    close(infd[1]);
    s_lame_fds[WRITE] = outfd[1];
    s_lame_fds[READ] = infd[0];
  }
  return 0;
}

これは、読み取り機能と書き込み機能です。write_lame_in でそれをしないでください。s_lame_fds[WRITE] の代わりに stderr に書き込むと、出力はほぼ即座に出力されるため、間違いなくラメを介したパイプになります。しかし、なぜ ?

static int
read_lame_out() 
{
  char buffer[READ_SIZE];
  memset(buffer, 0, sizeof(buffer));
  int i;
  int br = read(s_lame_fds[READ], buffer, sizeof(buffer) - 1);
  fprintf(stderr, "read %d bytes from lame out\n", br);
  return br;
}

static int
write_lame_in()
{
  int bytes_written;
  //bytes_written = write(2, s_data_buf, s_data_len);
  bytes_written = write(s_lame_fds[WRITE], s_data_buf, s_data_len);
  if (bytes_written > 0) {
    //fprintf(stderr, "%d bytes written\n", bytes_written);
    s_data_len -= bytes_written;
    fprintf(stderr, "data_len write: %d\n", s_data_len);
    memmove(s_data_buf, s_data_buf + bytes_written, s_data_len);
    if (s_data_len == 0) {
      fprintf(stderr, "finished\n");
    }
  } 

  return bytes_written;
}

static int
read_tcp_socket(struct connection_s *connection)
{
  char buffer[READ_SIZE];
  int bytes_read;
  bytes_read = connection_read(connection, buffer, sizeof(buffer)-1);
  if (bytes_read > 0) {
    //fprintf(stderr, "read %d bytes\n", bytes_read);
    if (s_data_len + bytes_read > sizeof(s_data_buf)) {
      fprintf(stderr, "BUFFER OVERFLOW\n");
      return -1;
    } else {
      memcpy(s_data_buf + s_data_len,
             buffer,
             bytes_read);
      s_data_len += bytes_read;
    }
    fprintf(stderr, "data_len: %d\n", s_data_len);
  }
  return bytes_read;
}

選択するものは、かなり基本的な選択ロジックです。もちろん、すべてのブロックはノンブロッキングです。

誰でもアイデアはありますか?助けていただければ幸いです;-)

4

3 に答える 3

3

おっと!LAME出力を確認しましたか?

特にあなたのコードを見てください

static char * const k_lame_args[] = {
  "--decode",
  "--mp3input",
  "-",
  "-",
  NULL
};

if (execv("/usr/local/bin/lame", k_lame_args) == -1) {

LAMEの場合のように、最初の引数()ではなく、誤って--decodeフラグを省略していることを意味します。あなたは使用する必要がありますargv[0]argv[1]

static char * const k_lame_args[] = {
  /* argv[0] */  "lame",
  /* argv[1] */  "--decode",
  /* argv[2] */  "--mp3input",
  /* argv[3] */  "-",
  /* argv[4] */  "-",
                 NULL
};

代わりは。

誤ってMP3オーディオを再圧縮しているため、速度が低下していると思います。--decode(これはほんの数分前に気付いたので、フラグを省略した場合にLAMEがそれを行うかどうかは確認していませんが、そうだと思います。)

于 2012-09-20T07:02:46.480 に答える
2

なんらかのブロッキングの問題がある可能性があります。ノンブロッキング パイプ (実際にはノンブロッキングではない) により、LAME がデータを消費するまでエンドがブロックされます。

別のアプローチを試していただけますか?通常のブロッキング パイプと、pthreads循環バッファから LAME にデータを書き込むという唯一の目的を持つ別のスレッド ( を使用) を使用します。その後、メイン スレッドは TCP/IP 接続からの循環バッファを満たし続け、バッファ レベルを簡単に追跡して報告することもできます。これは、開発およびデバッグ中に非常に役立ちます。一般に、非ブロック パイプよりもブロック パイプとスレッドの方がはるかに成功しています。

Linux では、スレッドのオーバーヘッドは実際にはそれほど大きくないため、組み込みアーキテクチャでも快適に使用できるはずです。マスターしなければならない唯一の秘訣は、ワーカー スレッドに適切なスタック サイズを指定することです。この場合、16384 バイトで十分です。プロセスに与えられた初期スタックのみが自動的に拡張され、スレッド スタックはデフォルトで非常に固定されるためです。大きい。

サンプルコードが必要ですか?

追加するために編集:

プログラムは、おそらく一定の速度で TCP/IP 接続からデータを受信します。ただし、LAME はデータを大きなチャンクで消費します。言い換えれば、この状況は、けん引されている車のようなもので、けん引車がけいれんしたり停止したりします。牽引車は毎回けん引されます。プロセスと LAME の両方が、ほとんどの場合、他のプロセスがより多くのデータを送受信するのを待っています。

于 2012-09-18T08:49:07.497 に答える
1

dup2まず、これらの 2 つのcloseは必要ありません (実際には、そうすべきではありません)

close(STDOUT_FILENO);
close(STDIN_FILENO);
于 2012-09-18T08:24:27.677 に答える