亲问一下javacv如何实现 ffmpeg 命令 -vcodec copy -acodec aac 一样的效果?
recorder.setVideoCodec(grabber.getVideoCodec());
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
recorder.start();
recorder.record(frame);
这样写cpu占用太高了;
如果直接用转封装的方式,又不能对音频重编码,导致视频无法播放
输入流类型如下:
大部分回答音频编码确实ok, 但是视频编码会变成h263, 如下:
谢邀。直截了当的讲,到目前为止JavaCV是不支持类似ffmpeg那样一边同时视频转封装(转复用),一边同时音频转码的功能。您可以通过javacpp-ffmpeg方式调用底层api方式实现,类似c++代码中的那样,但是复杂度提高很多
该回答引用ChatGPT
AVFormatContext *inputFormatContext = NULL;
AVFormatContext *outputFormatContext = NULL;
AVOutputFormat *outputFormat = NULL;
AVStream *inputStream = NULL;
AVStream *outputStream = NULL;
// 打开 RTSP 流
avformat_open_input(&inputFormatContext, "rtsp://xxx.xxx.xxx.xxx:xxxx", NULL, NULL);
// 查找音视频流
avformat_find_stream_info(inputFormatContext, NULL);
// 设置输出格式为 RTMP
avformat_alloc_output_context2(&outputFormatContext, NULL, "flv", "rtmp://xxx.xxx.xxx.xxx:xxxx");
// 添加音视频流
for (int i = 0; i < inputFormatContext->nb_streams; i++) {
inputStream = inputFormatContext->streams[i];
outputStream = avformat_new_stream(outputFormatContext, inputStream->codec->codec);
avcodec_copy_context(outputStream->codec, inputStream->codec);
outputStream->codec->codec_tag = 0;
if (outputFormatContext->oformat->flags & AVFMT_GLOBALHEADER) {
outputStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
// 打开输出流
avio_open(&outputFormatContext->pb, "rtmp://xxx.xxx.xxx.xxx:xxxx", AVIO_FLAG_WRITE);
avformat_write_header(outputFormatContext, NULL);
AVPacket packet;
AVFrame *frame = av_frame_alloc();
AVCodecContext *audioCodecContext = NULL;
AVCodec *audioCodec = NULL;
int audioStreamIndex = -1;
// 查找音频流
for (int i = 0; i < inputFormatContext->nb_streams; i++) {
if (inputFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
audioStreamIndex = i;
break;
}
}
// 打开音频编码器
audioCodecContext = inputFormatContext->streams[audioStreamIndex]->codec;
audioCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
avcodec_open2(audioCodecContext, audioCodec, NULL);
// 循环读取音视频帧
while (av_read_frame(inputFormatContext, &packet) >= 0) {
if (packet.stream_index == audioStreamIndex) {
// 对音频帧进行重编码
avcodec_send_packet(audioCodecContext, &packet);
while (avcodec_receive_frame(audioCodecContext, frame) == 0) {
frame->pts = av_rescale_q(frame->pts, audioCodecContext->time_base, outputStream->time_base);
frame->pkt_dts = frame->pts;
av_interleaved_write_frame(outputFormatContext, &packet);
av_frame_unref(frame);
}
} else {
// 直接写入视频帧
av_interleaved_write_frame(outputFormatContext, &packet);
}
av_packet_unref(&packet);
}
// 关闭输入输出流
av_write_trailer(outputFormatContext);
avformat_close_input(&inputFormatContext);
avio_close(outputFormatContext->pb);
avformat_free_context(outputFormatContext);
该回答引用GPT:要实现类似于FFmpeg命令“-vcodec copy -acodec aac”的效果,可以通过以下步骤使用JavaCV实现:
1 创建FFmpegFrameRecorder对象,并使用setFormat方法指定输出格式(例如:“flv”)。
2 使用setVideoCodec方法设置视频编码器为与输入流相同的编码器(grabber.getVideoCodec())。
3 使用setAudioCodec方法将音频编码器设置为AAC编码器(avcodec.AV_CODEC_ID_AAC)。
4 使用start方法启动recorder。
5 循环读取输入流,使用record方法将每个frame写入recorder。
6 使用stop方法停止recorder。
下面是示例代码:
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("rtsp://example.com/stream");
grabber.start();
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("rtmp://example.com/live/stream", grabber.getImageWidth(), grabber.getImageHeight());
recorder.setFormat("flv");
recorder.setVideoCodec(grabber.getVideoCodec());
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
recorder.start();
Frame frame = null;
while ((frame = grabber.grabFrame()) != null) {
recorder.record(frame);
}
recorder.stop();
grabber.stop();
请注意,将视频编码器设置为“与输入流相同的编码器”可能会导致视频无法播放,因为某些播放器可能无法解码该编码器。如果需要更广泛的兼容性,请使用H.264或VP8等常用编码器。如果您仍然需要使用与输入流相同的编码器,则可以尝试将recorder.setInterleaved(true)设置为true,这将启用交错模式,以便在视频帧之间保留输入流中的原始帧。
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
实现 ffmpeg 命令 -vcodec copy -acodec aac 一样的效果,可以使用 FFmpegFrameRecorder 类,并通过 FFmpeg 的编码器进行编码,具体如下:
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputUrl, inputWidth, inputHeight);
//设置视频编码参数
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 视频编码
recorder.setFormat("flv"); // 封装格式,如果需要推送到rtmp则需要设置为flv
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); // 像素格式
//设置音频编码参数
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 音频编码
recorder.setAudioChannels(2);
recorder.setSampleRate(44100);
recorder.start();
//如果想要不进行视频编码直接copy,可以使用如下方式
//grabber.setOption("vcodec", "copy");
Frame frame;
while ((frame = grabber.grab()) != null) {
recorder.record(frame);
}
recorder.stop();
注意事项:
可以尝试使用 javacv 提供的 FFmpegFrameRecorder 类来实现相应的功能,具体实现如下:
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("rtmp://xxx", grabber.getImageWidth(), grabber.getImageHeight(), 1);
recorder.setFrameRate(grabber.getFrameRate());
recorder.setFormat("flv");
recorder.setVideoOption("crf", "18");
recorder.setVideoOption("preset", "ultrafast");
recorder.setVideoCodecName("copy");
recorder.setAudioCodecName("aac");
recorder.setAudioOption("b", "128k");
recorder.setAudioQuality(0);
recorder.start();
Frame frame;
while ((frame = grabber.grab()) != null) {
recorder.record(frame);
}
recorder.stop();
在这里,我们指定了 recorder.setVideoCodecName("copy"),表示将视频流进行转封装,不进行重编码。而对于音频,我们则指定了 recorder.setAudioCodecName("aac"),表示将音频流进行 AAC 编码。
需要注意的是,由于音频和视频的编码方式不同,所以在进行音视频合并的时候,可能会出现音视频不同步的情况。如果出现这种情况,可以尝试对音频流进行时间戳调整,或者对音频流进行降采样等操作,以达到音视频同步的效果。
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("rtsp://your_stream_url");
grabber.start();
// 视频编码器
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("rtmp://your_rtmp_url", grabber.getImageWidth(), grabber.getImageHeight(), 1);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setFrameRate(grabber.getFrameRate());
recorder.setVideoBitrate(2000000);
recorder.setPixelFormat(grabber.getPixelFormat());
recorder.setVideoOption("tune", "zerolatency"); // 关闭编码器延迟优化
recorder.start();
// 音频编码器
FFmpegFrameRecorder audioRecorder = new FFmpegFrameRecorder("rtmp://your_rtmp_url", 0);
audioRecorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
audioRecorder.setAudioBitrate(96000);
audioRecorder.setAudioChannels(2);
audioRecorder.setSampleRate(44100);
audioRecorder.start();
Frame frame;
while ((frame = grabber.grab()) != null) {
if (frame.image != null) {
recorder.setTimestamp(grabber.getTimestamp());
recorder.record(frame);
}
if (frame.samples != null) {
audioRecorder.setTimestamp(grabber.getTimestamp());
audioRecorder.record(frame);
}
}
该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
可以使用FFmpegFrameRecorder类来实现 -vcodec copy -acodec aac 的效果,示例如下:
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, grabber.getImageWidth(), grabber.getImageHeight());
recorder.setFormat("mp4");
recorder.setFrameRate(grabber.getFrameRate());
recorder.setVideoCodec("copy");
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
recorder.setAudioChannels(grabber.getAudioChannels());
recorder.setSampleFormat(grabber.getSampleFormat());
recorder.setSampleRate(grabber.getSampleRate());
recorder.start();
Frame frame;
while ((frame = grabber.grab()) != null) {
recorder.record(frame);
}
recorder.stop();
其中,setVideoCodec("copy") 设置视频编码器为“拷贝”,即直接拷贝输入视频的编码格式;setAudioCodec(avcodec.AV_CODEC_ID_AAC) 设置音频编码器为AAC;其他的设置与输入流保持一致。
如果以上回答对您有所帮助,点击一下采纳该答案~谢谢
可以使用JavaCV的FFmpegFrameRecorder类来设置视频编码器和音频编码器,以实现类似于FFmpeg命令“-vcodec copy -acodec aac”的效果。
要设置视频编码器为“copy”,请将以下代码添加到您的记录器代码中:
recorder.setVideoCodec(avcodec.AV_CODEC_ID_COPY);
要设置音频编码器为“AAC”,请将以下代码添加到您的记录器代码中:
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
如果CPU使用率仍然很高,您可以尝试跳帧进行处理,这取决于您的应用程序的实现方式。另外,可能还需要调整视频帧大小和音频采样率等参数,以进一步优化性能和质量。
要实现与FFmpeg命令“-vcodec copy -acodec aac”类似的效果,需要将视频流直接复制到输出文件中,而同时重新编码音频流。可以使用FFmpeg提供的filtergraph实现该功能。以下是使用JavaCV实现该功能的代码示例:
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("input.mp4");
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output.mp4", grabber.getImageWidth(), grabber.getImageHeight());
recorder.setFrameRate(grabber.getFrameRate());
recorder.setFormat("mp4");
// 设置video filter
String videoFilter = "copy";
// 设置audio filter
String audioFilter = String.format("asetpts=PTS-STARTPTS,aresample=%d,aformat=channel_layouts=stereo,acodec=aac", grabber.getSampleRate());
recorder.setVideoOption("filter:v", videoFilter);
recorder.setAudioOption("filter:a", audioFilter);
recorder.start();
Frame frame;
while ((frame = grabber.grab()) != null) {
recorder.record(frame);
}
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
在上述代码中,首先通过FFmpegFrameGrabber获取视频流,并使用FFmpegFrameRecorder创建一个输出文件,然后设置视频的帧率、格式等参数。接着,我们通过设置video filter为“copy”,实现视频流直接复制到输出文件中。而对于音频流,则需要重新编码,因此我们使用了filtergraph实现了这一点。在audio filter中,使用“aresample”重新采样音频流,然后使用“aformat”将采样率设置为stereo并设置音频编码格式为aac。最后,通过while循环,将每一帧的视频和音频写入输出文件中。