簡體   English   中英

如何將音頻幀從 input.mp4 傳遞到 libavcodec 中的 output.mp4?

[英]How can I pass audio frames from an input .mp4 to an output .mp4 in libavcodec?

我有一個項目可以正確打開 a.mp4,提取視頻幀,修改它們,並將修改后的幀轉儲到 output.mp4。 一切正常(主要是 - 我有一個隨機彈出的視頻計時錯誤,但我會殺了它)除了音頻的編寫。 我根本不想修改音頻通道 - 我只想將 input.mp4 中的音頻原封不動地傳遞給 output.mp4。

這里有太多代碼無法提供一個工作示例,主要是因為那里有很多 OpenGL 和 GLSL,但最重要的部分是我推進框架的地方。 該方法在循環中調用,如果該幀是視頻幀,則循環將圖像數據發送到渲染硬件,對其執行一堆 GL 魔術,然后寫出一幀視頻。 如果幀是音頻幀,則循環不執行任何操作,但advance_frame()方法應該只是將該幀轉儲到 output mp4。 我不知道 libavcodec 提供了什么來做到這一點。

請注意,在這里,我將音頻數據包解碼為幀,但這不是必需的。 我寧願使用數據包而不是消耗 CPU 時間來進行解碼。 (我已經嘗試過另一種方式,但這就是我嘗試解碼數據時的結果,然后重新編碼以創建 output stream。)我只需要一種方法將數據包從輸入傳遞到output。

bool MediaContainerMgr::advance_frame() {
    int ret; // Crappy naming, but I'm using ffmpeg's name for it.
    while (true) {
        ret = av_read_frame(m_format_context, m_packet);
        if (ret < 0) {
            // Do we actually need to unref the packet if it failed?
            av_packet_unref(m_packet);
            if (ret == AVERROR_EOF) {
                finalize_output();
                return false;
            }
            continue;
            //return false;
        }
        else {
            int response = decode_packet();
            if (response != 0) {
                continue;
            }
            // If this was an audio packet, the image renderer doesn't care about it - just push
            // the audio data to the output .mp4:
            if (m_packet->stream_index == m_audio_stream_index) {
                printf("m_packet->stream_index: %d\n", m_packet->stream_index);
                printf("  m_packet->pts: %lld\n", m_packet->pts);
                printf("  mpacket->size: %d\n", m_packet->size);
                // m_recording is true if we're writing a .mp4, as opposed to just letting OpenGL
                // display the frames onscreen.
                if (m_recording) {
                    int err = 0;
                    // I've tried everything I can find to try to push the audio frame to the
                    // output .mp4. This doesn't work, but neither do a half-dozen other libavcodec
                    // methods:
                    err = avcodec_send_frame(m_output_audio_codec_context, m_last_audio_frame);

                    if (err) {
                        printf("  encoding error: %d\n", err);
                    }
                }
            }
            av_packet_unref(m_packet);
            if (m_packet->stream_index == m_video_stream_index) {
                return true;
            }
        }
    }
}

advance_frame()的主力是decode_packet() 所有這些都適用於視頻數據:

int MediaContainerMgr::decode_packet() {
    // Supply raw packet data as input to a decoder
    // https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga58bc4bf1e0ac59e27362597e467efff3
    int             response;
    AVCodecContext* codec_context = nullptr;
    AVFrame*        frame         = nullptr;

    if (m_packet->stream_index == m_video_stream_index) {
        codec_context = m_video_input_codec_context;
        frame = m_last_video_frame;
    }
    if (m_packet->stream_index == m_audio_stream_index) {
        codec_context = m_audio_input_codec_context;
        frame = m_last_audio_frame;
    }

    if (codec_context == nullptr) {
        return -1;
    }

    response = avcodec_send_packet(codec_context, m_packet);
    if (response < 0) {
        char buf[256];
        av_strerror(response, buf, 256);
        printf("Error while receiving a frame from the decoder: %s\n", buf);
        return response;
    }

    // Return decoded output data (into a frame) from a decoder
    // https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c
    response = avcodec_receive_frame(codec_context, frame);
    if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
        return response;
    } else if (response < 0) {
        char buf[256];
        av_strerror(response, buf, 256);
        printf("Error while receiving a frame from the decoder: %s\n", buf);
        return response;
    } else {
        printf(
            "Stream %d, Frame %d (type=%c, size=%d bytes), pts %lld, key_frame %d, [DTS %d]\n",
            m_packet->stream_index,
            codec_context->frame_number,
            av_get_picture_type_char(frame->pict_type),
            frame->pkt_size,
            frame->pts,
            frame->key_frame,
            frame->coded_picture_number
        );
    }
    return 0;
}

如有必要,我可以為所有上下文提供設置,但為簡潔起見,也許我們可以擺脫av_dump_format(m_output_format_context, 0, filename, 1)顯示的內容:

Output #0, mp4, to 'D:\yodeling_monkey_nuggets.mp4':
  Metadata:
    encoder         : Lavf58.64.100
    Stream #0:0: Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 1920x1080, q=-1--1, 20305 kb/s, 29.97 fps, 30k tbn
    Stream #0:1: Audio: aac (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 125 kb/s

要將音頻 AVPacket “按原樣”放入 output 而無需解碼 - 編碼步驟,您應該對此類數據包使用av_write_frame function 而不是avcodec_send_frame 請注意,這些函數使用不同的上下文: AVFormatContextAVCodecContext

avcodec_send_frame向編碼器提供原始視頻或音頻幀

av_write_frame將數據包直接傳遞給復用器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM