The Media Kit Table of Contents     The Media Kit Index

Reading and Writing Media Files

 BMediaFile及びBMediaTrackクラスを使用すれば、比較的苦労せずにメディアファイルを処理できるようになります。この節では、これらのクラスを使用して、メディアファイルをあるフォーマットから他のフォーマットに変換するサンプルプログラムを見ていきます。


Preparing a media_format

   void BuildMediaFormat(int32 width, int32 height, color_space cspace,
                media_format *format) {
      media_raw_video_format *rvf = &format->u.raw_video;
   
      memset(format, 0, sizeof(*format));
   
      format->type = B_MEDIA_RAW_VIDEO;
      rvf->last_active = (uint32)(height - 1);
      rvf->orientation = B_VIDEO_TOP_LEFT_RIGHT;
      rvf->pixel_width_aspect = 1;
      rvf->pixel_height_aspect = 3;
      rvf->display.format = cspace;
      rvf->display.line_width = (int32)width;
      rvf->display.line_count = (int32)height;
      if (cspace == B_RGB32)
         rvf->display.bytes_per_row = 4 * width;
      else {
         printf("can>t build the format!n");
         exit(5);
      }
   }

 BuildMediaFormat()は、ビデオフォーマット—ビデオの幅、高さ及び色空間(color space)—を記述するparameterを入力として受け取り、そのフォーマットを記述するmedia_format構造体を返します。私達の用途では、B_RGB32の色空間が要求され、フレームはrawなビデオフォーマットになるでしょう。


Converting the Files

 下記のtranscode()関数は、新しいファイルに新たに変換されたメディアを書込むことによって、実際にメディアファイルを他のフォーマットに変換する処理を扱います。私達は、荷を軽くするための下記のコード中に、それを見ることができます。

 transcode()は、オリジナルのファイルからのビデオトラックを参照するBMediaTrackを入力として受け取り、またオーディオトラックを参照する他のBMediaTrackを受け取ります。outputは、生成される新しいファイルの名前です。

 family_nameは、新しいファイルを生成する際に使用されるファイルフォーマットのfamilyを指定し、video_name及びaudio_nameは、ビデオ及びオーディオトラックに対してどのエンコーダが使用されるかを名前によって示します。

   void transcode(BMediaTrack *vidtrack, BMediaTrack *audtrack,
            char *output, char *family_name, char *video_name,
            char *audio_name) {
      char *chunk;
      char *bitmap = NULL, *sound_buffer = NULL;
      bool found_video_encoder = false, found_audio_encoder = false;
      bool found_family;
      int32 i, sz, cookie;
      int64 numFrames, j;
      int64 framesize;
      status_t err;
      entry_ref ref;
      BMediaFile out;
      BMediaTrack vid = NULL, *aud = NULL;
      media_format format, outfmt;
      media_codec_info mci;
      media_file_format mfi;
      media_header mh;
   
      err = get_ref_for_path(output, &ref);
      if (err) {
         printf("problem with get_ref_for_path() -- %sn", strerror(err));
         return;
      }

 この関数は、出力ファイルに対してentry_refを生成することから始まります。もしエラーが生じたら、この関数はエラーメッセージを表示した後に終了します。

      cookie = 0;
      while((err = get_next_file_format(&cookie, &mfi)) == B_OK) {
         if (strcmp(mfi.short_name, family_name) == 0)
            break;
      }
   
      if (err != B_OK) {
         printf("failed to find a file format handler !n");
         return;
      }

 次に、要求されたファイルフォーマットのfamilyの為の適切なハンドラを探すためにget_next_file_format()を呼び出すために、ループが使用されます。もし指定されたfamily_nameが全ての使用可能なファイルフォーマットのshort_nameフィールドに一致しなければ、エラーがプリントされ、関数は中断します。そうでなければ、mfiにファイルフォーマットの記述が収められます。

      out = new BMediaFile(&ref, &mfi);
      err = out->InitCheck();
      if (err != B_OK) {
         printf("failed to properly init the output file... (%s)n", strerror(err));
         delete out;
         return;
      }

 一旦適切なメディアファイルフォーマットが決定されたら、新しいBMediaFileが生成されます。出力ファイルのentry_refが指定され、選択されたメディアファイルフォーマットが提供されます。これは、トラックを持たない新しいメディアファイルを生成するでしょう。もしこれが失敗したら(BMediaFile::InitCheck()B_OK以外の何かを返したら)、エラーメッセージが表示され、BMediaFileが削除され、関数が返ります。

 次のコードは、新しいビデオトラックの生成を扱います。これはオリジナルのBMediaTrackへの参照であるvidtrackNULLでない場合のみ動作するということに留意して下さい。もしNULLであれば、ファイル中にビデオトラックがないと想定されます。

      if (vidtrack) {
         vidtrack->EncodedFormat(&format);
         
         if (video_name) {
            int width, height;
            width = format.u.encoded_video.output.display.line_width;
            height = format.u.encoded_video.output.display.line_count;
      
            memset(&format, 0, sizeof(format));
            BuildMediaFormat(width, height, B_RGB32, &format);
      
            vidtrack->DecodedFormat(&format);
            
            bitmap = (char *)malloc(width * height * 4);
      
            cookie = 0;
            while (get_next_encoder(&cookie, &mfi, &format, &outfmt, &mci) == B_OK) {
               printf("found encoder %s (%d)n", mci.pretty_name, mci.id);
               if (strcmp(video_name, mci.short_name) == 0) {
                  found_video_encoder = true;
                  break;
               }
            }
         }

 もしビデオトラックが存在すれば、BMediaTrack::EncodedFormat()を呼び出すことでエンコードされたフォーマットを決定します。返されたmedia_formatは、元ファイルのビデオフレームのフォーマットを記述します。

 もし出力用のビデオエンコーダが指定されたら、先程実装したBuildMediaFormat()関数を用いてmedia_formatがコンストラクトされます。このフォーマットは、元のファイルから得られたフレームをrawでエンコードされないビデオバッファに読み込むために使用されるでしょう。このformatが出力するフレームに使用されるということを指定して、元のビデオトラックに対してBMediaTrack::DecodedFormat()を呼び出します。この点からさらに進めば、vidtrackによって運ばれてきたフレームは、rawなビデオフォーマットになるでしょう。

 bitmapバッファが生成されます。これは、rawなビデオの各々のフレームがデコードされた後で、なおかつそのフレームが新しいファイルにエンコードされる前、即ちそれらがtranscodeされている間に、そのフレームを格納します。

 rawなビデオフォーマットを要求された出力フォーマットに変換できるエンコーダを配置するために、もう一つのループが使用されます。これはループ中で、mfiによって指定されたmedia_file_formatでデータを受け取り、formatによって指定されたmedia_formatにフレームを変換するように、get_next_encoder()を呼び出すことで行われます。エンコーダの記述は、mciに返されます。一致するものが見つかったら、それが入力時の引数によって要求されたエンコーダの名前であるvideo_nameに一致するかチェックします。もしこれが行われた場合、found_video_encoderフラグをtrueにセットすることで、それを受け入れます。

         if (found_video_encoder) 
            vid = out->CreateTrack(&format, &mci);
         else
            vid = out->CreateTrack(&format);
            
         if (vid == NULL) {
            printf("Failed to create video trackn");
            delete out;
            return;
         }
      }

 もしビデオエンコーダが見つかったら、そのエンコーダであるmciを使用することで新しいビデオトラックが生成されます。そうでなければ、rawなビデオトラックが生成されます。これは、ユーザがエンコードを全く望まない場合(ユーザがvideo_nameNULLを指定した場合)もカバーします。

 もしトラックの生成が不可能であれば、エラーメッセージが表示され、出力用のBMediaFileが削除され、関数が返ります。

 次に、オーディオトラックが同様の方法で用意されます。エンコードされたフォーマットが決定され、デコードされるフォーマットが指定され、そして新しいトラックの生成に続いてエンコーダが配置されます。sound_bufferは、ソースのトラックが読み込まれるのと同様に、返されたそれぞれのバッファに詰め込まれるオーディオデータの大きさを収められるように領域確保されます。

      if (audtrack) {
         audtrack->EncodedFormat(&format);
   
         audtrack->DecodedFormat(&format);
         sound_buffer = (char*)malloc(format.u.raw_audio.buffer_size);
         framesize = (format.u.raw_audio.format&15)*format.u.raw_audio.channel_count;
      
         if (audio_name) {
            cookie = 0;
            while (get_next_encoder(&cookie, &mfi, &format, &outfmt, &mci) == B_OK) {
               printf("found encoder %s (%d)n", mci.pretty_name, mci.id);
               if (strcmp(audio_name, mci.short_name) == 0) {
                  found_audio_encoder = true;
                  break;
               }
            }
         }
      
         if (found_audio_encoder) 
            aud = out->CreateTrack(&format, &mci);
         else
            aud = out->CreateTrack(&format);
            
         if (aud == NULL) {
            printf("Failed to create audio trackn");
            delete out;
            return;
         }
      }

 最後に、新しいファイルにヘッダを置きます。

      // Add the copyright and commit the header
      out->AddCopyright("Copyright 1999 Be Incorporated");
      out->CommitHeader();

 この例では、コピーライト情報がBMediaFile::AddCopyright()呼び出しによって付け加えられます。一旦全ての準備が整ったら、私達がファイルに対してまさにメディアデータを書き込み始めるところであることを示すために、BMediaFile::CommitHeader()を呼び出します。

 その後、もしトラックがひとつなら、ビデオトラックの書き込みが始まります。

      // Process the video track, if any
      if (vidtrack) {
         int is_key_frame = 0;
            
         if (found_video_encoder) {
            numFrames = vidtrack->CountFrames();
            for(j = 0; j < numFrames; j++) {
               int64 framecount = 1;
               printf("                                r");
               printf("processing frame: %5Ld", j);
               fflush(stdout);
               err = vidtrack->ReadFrames(bitmap, &framecount, &mh);
               if (err) {
                  printf("video: GetNextChunk error -- %sn", strerror(err));
                  break;
               }
               err = vid->WriteFrames(bitmap, 1, mh.u.encoded_video.field_flags);
               if (err) {
                  printf("err %s (0x%x) writing video frame %Ldn",
                        strerror(err), err, j);
                  break;
               }
            }

 もし使用するビデオエンコーダがあれば(出力データがエンコードされるなら)、BMediaTrack::CountFrames()を呼び出してビデオトラックのフレーム数を数え上げ、その後全てのフレームをループします。

 それぞれのフレームに対して、ユーザに何をしているか知らせるための状況通知を表示し、その後にBMediaTrack::ReadFrames()を呼び出してソースファイルから次のフレームを読み込みます。フレームが読み込まれるバッファとしてbitmapを指定し、framecountに読み込まれたフレームの数が収められます(ビデオトラックに関しては、常に1です)。media_headerであるmhは、バッファにあるデータの記述を受け取るでしょう。

 もしフレームを読み込む際にエラーが生じたら、エラーメッセージがプリントされ、ビデオの変換が中断されます。

 そうでなければ、出力ビデオトラックであるvidが、BMediaTrack::WriteFrames()呼び出しによって書込まれます。新しいフレームは自動的にエンコードされ、新しいファイルに付け加えられます。もしこれを行っている際にエラーが生じれば、エラーメッセージが表示され、処理が中断されます。

 もし使用されるエンコーダがなければ(出力ファイルがrawまたは不明のフォーマットデータとして書込まれるなら)、下記のコードが使用されます :

         } else {
            numFrames = vidtrack->CountFrames();
            for(j = 0; j < numFrames; j++) {
               printf("                                r");
               printf("processing frame: %5Ld", j);
               fflush(stdout);
   
               err = vidtrack->ReadChunk(&chunk, &sz, &mh);
               if (err) {
                  printf("video: GetNextChunk error -- %sn", strerror(err));
                  break;
               }
   
               err = vid->WriteChunk(chunk, sz, mh.u.encoded_video.field_flags);
               if (err) {
                  printf("err %s (0x%x) writing video frame %Ldn",
                        strerror(err), err, j);
                  break;
               }
            }
         }
         printf("r                                     r");
      }

 ここでの実際の相違は、BMediaTrack::ReadFrames()及びBMediaTrack::WriteFrames()を使用する代りに、BMediaTrack::ReadChunk()及びBMediaTrack::WriteChunk()を使用するということです。これらはまったくデータを解釈するよう意図せず、メディアデータを処理します。

 これらのループのうち1つまたはその他のループは、エラーが生じるか或いはビデオトラック全体を変換するまで動作し続けます。

 次に、もしオーディオトラックがあれば、それが変換されます。見ていただければ分かるように、受け取ったバッファがそれぞれひとつ以上のオーディオフレームを持つことを除いて、これは厳密にほぼ同じ方法で行われます。

      // Process the audio track, if any
      if (audtrack) {
         int64       framecount = 0;
   
         if (found_audio_encoder) {
            // Decode and encode all the frames
            numFrames = audtrack->CountFrames();
            printf("Total frame count : %Ldn", numFrames);
            for (j = 0; j < numFrames; j+=framecount) {
               err = audtrack->ReadFrames(sound_buffer, &framecount, &mh);
               if (err) {
                  printf("video: GetNextChunk error -- %sn", strerror(err));
                  break;
               }
   
               err = aud->WriteFrames(sound_buffer, framecount);
               if (err) {
                  printf("err %s (0x%x) writing audio frame %Ldn",
                        strerror(err), err, j);
                  break;
               }
            }
         } else {
            printf("processing chunks...n");
            while (true) {
               err = audtrack->ReadFrames(sound_buffer, &framecount, &mh);
               if (err) {
                  printf("audio: GetNextChunk error -- %sn", strerror(err));
                  break;
               }
      
               err = aud->WriteChunk(sound_buffer, framecount*framesize);
               if (err) {
                  printf("err %s (0x%x) writing audio chunk %Ldn",
                        strerror(err), err, j);
                  break;
               }
            }
         }
         printf("r                                     r");
      }

 一旦オーディオの変換が終了したら、私達が領域確保したビデオとオーディオのバッファを解放し、出力BMediaFileを閉じてそれを削除する他に、残された作業はほとんどありません。

      if (bitmap)
         free(bitmap);
      if (sound_buffer)
         free(sound_buffer);
   
      out->CloseFile();
      delete out;
      out = NULL;
   }

 この関数が返された後、呼び出し側はソースのBMediaFileを削除する責任があります。


Using the transcode() Function

 メディアファイルをあるフォーマットから他のフォーマットに変換するコマンドラインユーティリティを提供する、transcode()関数を使用したmain()を見て下さい。

   int main(int argc, char **argv) {
      status_t err;
      entry_ref ref;
      media_format format;
      BMediaFile mediaFile;
      BMediaTrack *track = NULL, *vidtrack = NULL, *audtrack = NULL;
      int32 i, numTracks;
      char *input = NULL, *output = NULL;
      char *video_encoder_name = NULL, *audio_encoder_name = NULL;
      char *family_name = NULL;
   
      if (argc < 2) {
         printf("usage: %s [-info][-avi|-qt][-wav][-aiff][-v <encoder_name>][-a <encoder_name>] <filename> [<output>]n", argv[0]);
         return 1;
      }

 引数の数が2未満であれば、使用方法の情報がプリントされます。

      for (i=1; i < argc; i++) {
         if (strcmp(&argv[i][0], "-info") == 0) {
            dump_info();
            exit(0);
         } else if (strcmp(&argv[i][0], "-avi") == 0 ||
                  strcmp(&argv[i][0], "-wav") == 0 ||
                  strcmp(&argv[i][0], "-aiff") == 0 ||
                  strcmp(&argv[i][0], "-quicktime") == 0) {
            family_name = &argv[i][1];
         } else if (strcmp(&argv[i][0], "-qt") == 0) {
            family_name = "quicktime";
         } else if (strcmp(&argv[i][0], "-v") == 0 && argv[i+1]) {
            video_encoder_name = argv[i+1];
            i++;
         } else if (strcmp(&argv[i][0], "-a") == 0 && argv[i+1]) {
            audio_encoder_name = argv[i+1];
            i++;
         } else if (input == NULL) {
            input = &argv[i][0];
         } else if (output == NULL) {
            output = &argv[i][0];
         } else {
            printf("%s: extra argument %sn", &argv[0][0], &argv[i][0]);
         }
      }

 引数は、ここで解釈されます。引数は :

  • -info: 使用できるメディアファイルフォーマットとエンコードに関する情報をダンプします。この関数は、get_next_file_format()関数の記述の中に見ることができます。

  • -avi: 出力ファイルをAVIフォーマットに指定します。これはfamily_nameを"avi"にセットします。

  • -wav: 出力ファイルをWAVフォーマットに指定します。これはfamily_nameを"wav"にセットします。

  • -aiff: 出力ファイルをAIFFフォーマットに指定します。これはfamily_nameを"aiff"にセットします。

  • -quicktime: 出力ファイルをQuickTimeムービーに指定します。これはfamily_nameを"quicktime"にセットします。

  • -qt: -quicktimeの略式です。

  • -v: "-v myformat"といった具合に、上記以外のビデオエンコーダ名を指定します。

  • -a: "-a audiowonderness"といった具合に、上記以外のオーディオエンコーダ名を指定します。

     inputは入力ファイルの名前であり、outputは生成される新しいメディアファイルの名前です。

          if (output == NULL)
             output = "output";
       
          err = get_ref_for_path(input, &ref);
          if (err) {
             printf("problem with get_ref_for_path() -- %sn", strerror(err));
             return 1;
          }

     もし出力ファイルの名前が指定されなければ、名前として"output"が想定されます。入力ファイルを参照するentry_refがコンストラクトされます。もしファイルが見つからなければ、エラーメッセージがプリントされ、プログラムが中断されます。

          mediaFile = new BMediaFile(&ref);
          err = mediaFile->InitCheck();
          if (err) {
             printf("cannot contruct BMediaFile object -- %sn", strerror(err));
             return 1;
          }

     その後、inputファイルを参照するBMediaFileのインスタンスが生成されます。もしエラーが生じたら、エラーが表示されてプログラムが中断されます。

          numTracks = mediaFile->CountTracks();
          printf("%s has %d media tracksn", input, numTracks);
          const char *copyright = mediaFile->Copyright();
          if (copyright)
             printf("#### copyright info: %sn", copyright);

     ソースファイルにあるトラックの数はBMediaFile::CountTracks()を呼び出すことによって得られ、ファイルの著作権情報はBMediaFile::Copyright()を呼び出すことで得られます。この情報はユーザに読めるようにプリントされます。

          for(i=0; i < numTracks; i++) {
             track = mediaFile->TrackAt(i);
             if (!track) {
                printf("cannot get track %d?!?n", i);
                return 1;
             }
       
             // get the encoded format
             err = track->EncodedFormat(&format);
             if (err) {
                printf("BMediaTrack::EncodedFormat error -- %sn", strerror(err));
                return 1;
             }
       
             if (format.type == B_MEDIA_RAW_VIDEO ||
                format.type == B_MEDIA_ENCODED_VIDEO) {
                
                vidtrack = track;
             } else if (format.type == B_MEDIA_RAW_AUDIO ||
                      format.type == B_MEDIA_ENCODED_AUDIO) {
       
                audtrack = track;
             } else {
                mediaFile->ReleaseTrack(track);
                track = NULL;
             }
          }

     次に、ループは、トラックを参照するBMediaTrackオブジェクトを得るために、ファイルに含まれる全てのトラックをBMediaFile::TrackAt()呼び出しによってひとつずつ繰り返し処理します。

     これは、変換されるビデオ及びオーディオトラックを全て探しだすのに役立ちます。ほとんどのムービーはいずれかひとつのみトラックを持ちますが、変換する際には無視する必要のある他の情報トラックを持つかも知れません。

          if (vidtrack == NULL && audtrack == NULL) {
             printf("%s has no audio or video tracks?!?n", input);
             return 1;
          }

     もしビデオトラックもオーディオトラックもなければ、ソースファイルは空であり、変換する価値がないため、エラーメッセージが表示され、プログラムは中断されます。

          if (family_name == NULL && vidtrack == NULL)
             family_name = "wav";
          else if (family_name == NULL)
             family_name = "quicktime";

     もしユーザが出力ファイルのfamilyを指定していなければ、ファイルはビデオトラックを持たず、WAVファイルフォーマットが想定されます。この部分で、"wab"を"aiff"に変更することによって、オーディオのみのファイルに対してAIFFを想定するようプログラムを変更できます。

     ビデオトラックを持つ任意のファイルに対して、もしfamilyがユーザによって指定されていなければ、QuickTimeフォーマットが想定されます。繰り返しになりますが、もしデフォルトで他のフォーマットになるようにしたいなら、ここで変更することができます。

          transcode(vidtrack, audtrack, output, family_name,
                     video_encoder_name, audio_encoder_name);

     変換は、実際に作業を行うためのtranscode()にこれら全てのparameterを渡すことで行われます。

          delete mediaFile;
       
          return 0;
       }

     transcode()が返ってきたら、単純にソースファイルを削除してプログラムを抜けます。(transcode()でエラーが生じなかったと仮定すると)ファイルは変換されています。


    Integrating Into a Real Application

     実際にアプリケーションを作る場合、それぞれのフォーマットに対してエンコーダが提供されるとの同じように、使用できる多様なメディアファイルフォーマットのリストをユーザが概観する手段を提供する必要があるでしょう。get_next_file_format()関数の記述内部で討議されるdump_info()サンプル関数は、これをどうやって可能にするかを表します。


    The Media Kit Table of Contents     The Media Kit Index


    The Be Book,
    ...in lovely HTML...
    for BeOS Release 5.

    Copyright © 2000 Be, Inc. All rights reserved..