c - 如何将音频帧从输入 .mp4 传递到 libavcodec 中的输出 .mp4?

标签 c audio ffmpeg encoding libavcodec

我有一个项目可以正确打开.mp4,提取视频帧,修改它们,并将修改后的帧转储到输出.mp4。一切正常(主要是 - 我有一个随机弹出的视频计时错误,但我会杀了它)除了音频的编写。我根本不想修改音频 channel - 我只想将输入 .mp4 中的音频原封不动地传递到输出 .mp4。
这里有太多代码无法提供一个工作示例,主要是因为那里有很多 OpenGL 和 GLSL,但最重要的部分是我推进一帧的地方。该方法在循环中调用,如果该帧是视频帧,则循环将图像数据发送到渲染硬件,对其执行一堆 GL 魔术,然后写出一帧视频。如果帧是音频帧,则循环不执行任何操作,但 advance_frame()方法应该只是将该帧转储到输出 mp4。我不知道 libavcodec 提供了什么来做到这一点。
请注意,在这里,我将音频数据包解码为帧,但这不是必需的。我宁愿使用数据包而不是消耗 CPU 时间来进行解码。 (我已经尝试过另一种方式,但这就是我尝试解码数据,然后重新编码以创建输出流时的结果。)我只需要一种方法将数据包从输入传递到输出。

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 “原样”输出到输出,您应该使用 av_write_frame此类数据包的函数,而不是 avcodec_send_frame .请注意,这些函数使用不同的上下文:AVFormatContextAVCodecContext .avcodec_send_frame向编码器提供原始视频或音频帧av_write_frame将数据包直接传递给复用器

关于c - 如何将音频帧从输入 .mp4 传递到 libavcodec 中的输出 .mp4?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67631132/

相关文章:

c - 如何使用指针对结构数组进行排序?

ios - SDL 在 iOS 上保存屏幕截图

video - 在 ffmpeg 中可靠地获取 PTS 值?

c - 设置 R 包中动态链接库的目录路径

启用功能时代码行为不当(可能的大小限制)

c - 多维指针数组

actionscript-3 - TypeError 1009和AS3中的静音

ios - 在iOS库中使用哪种格式播放视频或音频?

javascript - 屏幕关闭时,HTML5 音频/javascript 在 android(谷歌浏览器)上停止工作

iphone - 构建应用程序时出错 : Undefined symbols for architecture armv7