8

この例から MP4 ビデオを作成する方法を学んでいます。問題は、この例が、オンザフライで生成されたダミー ソース データからのオーディオ エンコーディングを示していることです。ファイルからオーディオをエンコードする必要があります。多くの例を確認しましたが、ほとんどの例は同じまたは別のオーディオ エンコーディングです。試行錯誤の過程で、オーディオ フレームとビデオ フレームの両方に同じ AVFormatContext を使用しています。それが正しいことなのか、それとも 2 つの別個のコンテキストを使用する必要があるのか​​ わかりません。これまでのところ、ビデオのエンコードは問題ありませんが、オーディオ ストリームは失敗します。 AVPacket が正しいオーディオ ストリーム インデックスを見つけられないためです。オーディオストリームをセットアップする方法は次のとおりです。

  void open_audio(AVFormatContext *oc, AVCodec **codec, AVStream **st ,enum AVCodecID codec_id){

    //    AVCodecContext *c;
    int ret;
    //    c = st->codec;

    *codec = avcodec_find_encoder(codec_id);
    if (!(*codec)) {
        fprintf(stderr, "Could not find encoder for '%s'\n",avcodec_get_name(codec_id));

    }
    /* open it */



    if(avformat_open_input(&oc,_audioInName.c_str(),NULL,NULL) !=0){

        Msg::PrintErrorMsg("Error opening audio file");

    }


    AVStream* audioStream = NULL;

    // Find the audio stream (some container files can have multiple streams in them)

    for (uint32_t i = 0; i < oc->nb_streams; ++i)

    {

        if (oc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)

        {

            audioStream = oc->streams[i];

            break;

        }

    }

    if (audioStream == NULL)
    {
        Msg::PrintErrorMsg("Could not find any audio stream in the file");

    }
    *st =audioStream;

    AVCodecContext *c  = audioStream->codec;
    c->codec = *codec;//avcodec_find_decoder(c->codec_id);
    audioStream->id = 1;
    c->sample_fmt  = AV_SAMPLE_FMT_S16;
    c->bit_rate    = 64000;
    c->sample_rate = 44100;
    c->channels    = 1;

    if (oc->oformat->flags & AVFMT_GLOBALHEADER){
        c->flags |= CODEC_FLAG_GLOBAL_HEADER;

    }

    if (c->codec == NULL)
    {
        Msg::PrintErrorMsg("Couldn't find a proper decoder");

    }

    ret = avcodec_open2(c, *codec, NULL);
    if (ret < 0) {

        Msg::PrintErrorMsg("Could not open audio codec\n");

    }

}

ここで「oc」は、ビデオ ストリームの初期化にも使用されるコンテキストと同じです。

次に、次のようなオーディオ フレームを書き込もうとしています。

  void write_audio_frame(AVFormatContext *oc, AVStream *st){
    AVCodecContext *c;
    AVPacket pkt = { 0 }; // data and size must be 0;
    AVFrame *frame = avcodec_alloc_frame();
    int got_packet, ret;
    av_init_packet(&pkt);
    c = st->codec;
    /////
    //  get_audio_frame(samples, audio_input_frame_size, c->channels);

    ////Read the packet:
    while(av_read_frame(oc,&pkt) == 0 ){

        if(pkt.stream_index ==st->index){

        // Try to decode the packet into a frame
        int frameFinished = 0;
        avcodec_decode_audio4(c, frame, &frameFinished, &pkt);

        // Some frames rely on multiple packets, so we have to make sure the frame is finished before
        // we can use it
        if (frameFinished){
            assert(frameFinished);
            ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
            if (ret < 0) {
                Msg::PrintErrorMsg("Error encoding audio frame\n");

            }
            if (!got_packet){
                printf("failed to aquire packet");
            }
            pkt.stream_index = st->index;
            /* Write the compressed frame to the media file. */
            ret = av_interleaved_write_frame(oc, &pkt);
            if (ret != 0) {

                Msg::PrintErrorMsg("Error while writing audio frame.");
            }

          }
        }

       }
    }
    av_free_packet(&pkt);
    avcodec_free_frame(&frame);
}

問題は、「if(pkt.stream_index ==st->index)」というステートメントを決して渡さないことです。パケット ストリーム インデックスがオーディオ ストリーム インデックスと等しくなることはありません。どこが間違っているか指摘できますか?

アップデート:

エンコーディング用に入力オーディオ ストリームを開くことはできましたが、オーディオ ストリームとビデオ ストリームを単一の出力にエンコードすることはできません。オーディオではまったく機能しません。

これが私がそれを使用する方法です:

   while(frame_count < _streamDurationNBFrames-1){

        uint8_t *frameToWrite =_frames.front();


        // Compute current audio and video time. ///

        if (audio_st){
            audio_pts = (double)audioIn_st->pts.val * audioIn_st->time_base.num / audioIn_st->time_base.den;
        }
        else{

            audio_pts = 0.0;
        }
        if (video_st){

            video_pts = (double)video_st->pts.val * video_st->time_base.num /   video_st->time_base.den;

        }else{
            video_pts = 0.0;
        }


        if ((!audio_st || audio_pts >= _streamDuration) && (!video_st || video_pts >= _streamDuration)){

            break;

        }


        if (audio_st && audio_pts < video_pts) {
            av_read_frame(informat, &pkt);//read audio from input stream
             Msg::PrintMsg("Encode audio here...");

          //==================   AUDIO ENCODE HERE   


           outpkt.data = pkt.data;
           outpkt.size = pkt.size;
           outpkt.stream_index = pkt.stream_index;
           outpkt.flags |= AV_PKT_FLAG_KEY;
           outpkt.pts = pkt.pts;
           outpkt.dts =pkt.dts;
           if(av_interleaved_write_frame(oc, &outpkt) < 0)
           {
            Msg::PrintErrorMsg("Fail Audio Write ");
           }
           else
           {
               audio_st->codec->frame_number++;
           }
           av_free_packet(&outpkt);
           av_free_packet(&pkt);



         }else{
          //==================   VIDEO  ENCODE HERE   

            write_video_frame(oc, video_st,frameToWrite);

            frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
         }

        ///at last delete this frame:
        _frames.pop();
        delete frameToWrite; ///deallocate the written frame!
    }

どういうわけか、オーディオ エンコーディング ループに入ると、audio_pts は video_pts に到達せず、常にゼロになります。

audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den; is always zero because  (double)audio_st->pts.val  returns zero.

だから基本的に私は再び同じ質問をしています: オーディオが外部ファイルから来るときに多重化を行う方法は?

ところで、以下の回答は、オーディオとビデオの両方のストリームが同じファイルからのものであると想定しているため、役に立ちませんが、私の場合はオーディオのみが外部ソースからのものです。

4

2 に答える 2