引入maven依赖
<!--<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.4.4</version>
</dependency>-->
<!-- javacv 和 ffmpeg -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.1-1.4.4</version>
</dependency>
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg-platform</artifactId>
<version>4.1-1.4.4</version>
</dependency>
代码实现
@Override
public void run() {
String inputFile=rtspUrl;
String outputFile=rtmpUrl;
String rtmpUrl2="";
String host="127.0.0.1";
String host2="127.0.0.1";
int port=1935;
int port2=1936;
long startTime1=0;
try {
if(!JavaCVUtil.isHostConnectable(host,port)){
System.out.println(id+"--->服务器或端口未打开");
throw new Exception(id+"--->服务器或端口未打开");
}
ConvertVideoPakcet videoPakcet=new ConvertVideoPakcet();
FFmpegFrameGrabber grabber=videoPakcet.from(inputFile);
System.out.println(id+"--->打开拉流器");
FFmpegFrameRecorderPlus record=videoPakcet.to(outputFile);
System.out.println(id+"--->打开录制器/推流器1");
FFmpegFrameRecorderPlus record2=null;
long err_index = 0;
int no_frame_index=0;
System.out.println(id+"--->开始推流");
avcodec.AVPacket pkt=null;
avcodec.AVPacket pkt2=null;
int f=0;
long startTime=System.currentTimeMillis();
long dts=90000;
dts=AV_TIME_BASE;
long dts1=0;
startTime1=startTime;
while (flag){
if(flag3&&f==0){
try {
if(!JavaCVUtil.isHostConnectable(host2,port2)){
throw new Exception(id+"--->服务器或端口未打开");
}
record2=videoPakcet.to(rtmpUrl2);
System.out.println("打开公网推流器");
f=1;
}catch (Exception e){
System.out.println(e.getMessage());
}
}
if(!flag3&&f==1){
f=0;
if(record2!=null){
record2.stop();
record2.release();
}
}
if(err_index>0||no_frame_index>=5){
break;
}
pkt=null;
try {
pkt=grabber.grabPacket();
if(pkt==null||pkt.size()<=0||pkt.data()==null) {
no_frame_index++;
System.out.println(id+"---->空--->"+no_frame_index);
continue;
}
if(flag3&&f==1){
pkt2=new avcodec.AVPacket();
av_packet_ref(pkt2, pkt);
err_index+=(record.recordPacket(pkt)?0:1);
try{
record2.recordPacket(pkt2);
}catch (Exception e){
System.out.println("record2发送失败,请检测远程服务器是否打开或查看本地录制器是否正常");
}
}else{
err_index+=(record.recordPacket(pkt)?0:1);
}
} catch (Exception e) {
e.printStackTrace();
err_index++;
}
Thread.sleep(10);
}
long endTime=System.currentTimeMillis();
long cha=endTime-startTime1;
System.out.println(id+"---->推流结束----->运行时间="+ cha/ 1000 / 60+"分");
System.exit(0);
flag=false;
grabber.stop();
record.stop();
record.release();
if(record2!=null){
record2.stop();
record2.release();
}
} catch (Exception e) {
long endTime=System.currentTimeMillis();
long cha=endTime-startTime1;
System.out.println(id+"---->推流异常>----->运行时间="+ cha/ 1000 / 60+"分");
System.exit(0);
flag=false;
}
}
转流器
import com.jiyuyun.util.JavaCVUtil;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.avformat;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import java.io.IOException;
import static org.bytedeco.javacpp.avcodec.AV_CODEC_ID_H264;
public class ConvertVideoPakcet {
FFmpegFrameGrabber grabber = null;
FFmpegFrameRecorderPlus record = null;
int width = -1, height = -1;
protected double framerate;
protected int bitrate;
public FFmpegFrameGrabber from(String src) throws Exception {
grabber = new FFmpegFrameGrabber(src);
grabber.setOption("fflags", "nobuffer");
grabber.setOption(JavaCVUtil.TimeoutOption.STIMEOUT.getKey(), String.valueOf(10 * 1000000));
if(src.indexOf("rtsp")>=0) {
grabber.setOption("rtsp_transport","tcp");
}
grabber.start();
if (width < 0 || height < 0) {
width = grabber.getImageWidth();
height = grabber.getImageHeight();
}
framerate = grabber.getVideoFrameRate();
bitrate = grabber.getVideoBitrate();
return grabber;
}
public FFmpegFrameRecorderPlus to(String out) throws Exception {
record = new FFmpegFrameRecorderPlus(out, width, height);
record.setGopSize(2);
record.setFrameRate(framerate);
record.setVideoBitrate(bitrate);
record.setAudioChannels(0);
avformat.AVFormatContext fc = null;
if (out.indexOf("rtmp") >= 0 || out.indexOf("flv") > 0) {
record.setFormat("flv");
record.setVideoCodec(AV_CODEC_ID_H264);
fc = grabber.getFormatContext();
}
record.start(fc);
return record;
}
public FFmpegFrameRecorderPlus to2(String out) throws Exception {
record = new FFmpegFrameRecorderPlus(out, width, height);
record.setGopSize(2);
record.setFrameRate(framerate);
record.setVideoBitrate(bitrate);
record.setAudioChannels(0);
avformat.AVFormatContext fc = null;
if (out.indexOf("rtmp") >= 0 || out.indexOf("flv") > 0) {
record.setFormat("flv");
}
return record;
}
public static void main(String[] args) {
String inuptFile="rtsp://admin:[email protected]:554/cam/realmonitor?channel=1&subtype=0";
String outputFile="rtmp://127.0.0.1:1935/hls/room";
boolean stat=true;
try {
ConvertVideoPakcet videoPakcet=new ConvertVideoPakcet();
FFmpegFrameGrabber grabber=videoPakcet.from(inuptFile);
FFmpegFrameRecorderPlus record=videoPakcet.to(outputFile);
long err_index = 0;
int no_frame_index=0;
while (stat && (no_frame_index<5 || err_index<1)){
avcodec.AVPacket pkt=null;
try {
pkt=grabber.grabPacket();
if(pkt==null||pkt.size()<=0||pkt.data()==null) {
System.out.println("空");
no_frame_index++;
continue;
}
boolean flg=record.recordPacket(pkt);
err_index+=(flg?0:1);
}catch (Exception e) {
e.printStackTrace();
err_index++;
}
}
grabber.stop();
record.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
重写FFmpegFrameRecorderPlus
package com.jiyuyun.help;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.avcodec.*;
import org.bytedeco.javacpp.avformat.*;
import org.bytedeco.javacpp.avutil.*;
import org.bytedeco.javacpp.swresample.*;
import org.bytedeco.javacpp.swscale.*;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FFmpegLockCallback;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameRecorder;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import static org.bytedeco.javacpp.avcodec.*;
import static org.bytedeco.javacpp.avdevice.avdevice_register_all;
import static org.bytedeco.javacpp.avformat.*;
import static org.bytedeco.javacpp.avutil.*;
import static org.bytedeco.javacpp.swresample.*;
import static org.bytedeco.javacpp.swscale.*;
public class FFmpegFrameRecorderPlus extends FrameRecorder {
public static FFmpegFrameRecorder createDefault(File f, int w, int h) throws Exception { return new FFmpegFrameRecorder(f, w, h); }
public static FFmpegFrameRecorder createDefault(String f, int w, int h) throws Exception { return new FFmpegFrameRecorder(f, w, h); }
private static Exception loadingException = null;
public static void tryLoad() throws Exception {
if (loadingException != null) {
throw loadingException;
} else {
try {
Loader.load(avutil.class);
Loader.load(swresample.class);
Loader.load(avcodec.class);
Loader.load(avformat.class);
Loader.load(swscale.class);
av_jni_set_java_vm(Loader.getJavaVM(), null);
avcodec_register_all();
av_register_all();
avformat_network_init();
Loader.load(avdevice.class);
avdevice_register_all();
} catch (Throwable t) {
if (t instanceof Exception) {
throw loadingException = (Exception)t;
} else {
throw loadingException = new Exception("Failed to load " + FFmpegFrameRecorder.class, t);
}
}
}
}
static {
try {
tryLoad();
FFmpegLockCallback.init();
} catch (Exception ex) { }
}
public FFmpegFrameRecorderPlus(String filename, int imageWidth, int imageHeight) {
this(filename, imageWidth, imageHeight, 0);
}
public FFmpegFrameRecorderPlus(File file, int imageWidth, int imageHeight, int audioChannels) {
this(file.getAbsolutePath(), imageWidth, imageHeight, audioChannels);
}
public FFmpegFrameRecorderPlus(String filename, int imageWidth, int imageHeight, int audioChannels) {
this.filename = filename;
this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
this.audioChannels = audioChannels;
this.pixelFormat = AV_PIX_FMT_NONE;
this.videoCodec = AV_CODEC_ID_NONE;
this.videoBitrate = 400000;
this.frameRate = 30;
this.sampleFormat = AV_SAMPLE_FMT_NONE;
this.audioCodec = AV_CODEC_ID_NONE;
this.audioBitrate = 64000;
this.sampleRate = 44100;
this.interleaved = true;
this.video_pkt = new AVPacket();
this.audio_pkt = new AVPacket();
}
public void release() throws Exception {
if (video_c != null) {
avcodec_free_context(video_c);
video_c = null;
}
if (audio_c != null) {
avcodec_free_context(audio_c);
audio_c = null;
}
if (picture_buf != null) {
av_free(picture_buf);
picture_buf = null;
}
if (picture != null) {
av_frame_free(picture);
picture = null;
}
if (tmp_picture != null) {
av_frame_free(tmp_picture);
tmp_picture = null;
}
if (video_outbuf != null) {
av_free(video_outbuf);
video_outbuf = null;
}
if (frame != null) {
av_frame_free(frame);
frame = null;
}
if (samples_out != null) {
for (int i = 0; i < samples_out.length; i++) {
av_free(samples_out[i].position(0));
}
samples_out = null;
}
if (audio_outbuf != null) {
av_free(audio_outbuf);
audio_outbuf = null;
}
if (video_st != null && video_st.metadata() != null) {
av_dict_free(video_st.metadata());
video_st.metadata(null);
}
if (audio_st != null && audio_st.metadata() != null) {
av_dict_free(audio_st.metadata());
audio_st.metadata(null);
}
video_st = null;
audio_st = null;
filename = null;
AVFormatContext outputStreamKey = oc;
if (oc != null && !oc.isNull()) {
if (outputStream == null && (oformat.flags() & AVFMT_NOFILE) == 0) {
avio_close(oc.pb());
}
int nb_streams = oc.nb_streams();
for(int i = 0; i < nb_streams; i++) {
av_free(oc.streams(i).codec());
av_free(oc.streams(i));
}
if (oc.metadata() != null) {
av_dict_free(oc.metadata());
oc.metadata(null);
}
av_free(oc);
oc = null;
}
if (img_convert_ctx != null) {
sws_freeContext(img_convert_ctx);
img_convert_ctx = null;
}
if (samples_convert_ctx != null) {
swr_free(samples_convert_ctx);
samples_convert_ctx = null;
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException ex) {
throw new Exception("Error on OutputStream.close(): ", ex);
} finally {
outputStream = null;
outputStreams.remove(outputStreamKey);
if (avio != null) {
if (avio.buffer() != null) {
av_free(avio.buffer());
avio.buffer(null);
}
av_free(avio);
avio = null;
}
}
}
}
@Override protected void finalize() throws Throwable {
super.finalize();
release();
}
static Map<Pointer,OutputStream> outputStreams = Collections.synchronizedMap(new HashMap<Pointer,OutputStream>());
static class WriteCallback extends Write_packet_Pointer_BytePointer_int {
@Override public int call(Pointer opaque, BytePointer buf, int buf_size) {
try {
byte[] b = new byte[buf_size];
OutputStream os = outputStreams.get(opaque);
buf.get(b, 0, buf_size);
os.write(b, 0, buf_size);
return buf_size;
}
catch (Throwable t) {
System.err.println("Error on OutputStream.write(): " + t);
return -1;
}
}
}
static WriteCallback writeCallback = new WriteCallback();
private OutputStream outputStream;
private AVIOContext avio;
private String filename;
private AVFrame picture, tmp_picture;
private BytePointer picture_buf;
private BytePointer video_outbuf;
private int video_outbuf_size;
private AVFrame frame;
private Pointer[] samples_in;
private BytePointer[] samples_out;
private PointerPointer samples_in_ptr;
private PointerPointer samples_out_ptr;
private BytePointer audio_outbuf;
private int audio_outbuf_size;
private int audio_input_frame_size;
private AVOutputFormat oformat;
private AVFormatContext oc;
private AVCodec video_codec, audio_codec;
private AVCodecContext video_c, audio_c;
private AVStream video_st, audio_st;
private SwsContext img_convert_ctx;
private SwrContext samples_convert_ctx;
private int samples_channels, samples_format, samples_rate;
private AVPacket video_pkt, audio_pkt;
private int[] got_video_packet, got_audio_packet;
private AVFormatContext ifmt_ctx;
@Override public int getFrameNumber() {
return picture == null ? super.getFrameNumber() : (int)picture.pts();
}
@Override public void setFrameNumber(int frameNumber) {
if (picture == null) { super.setFrameNumber(frameNumber); } else { picture.pts(frameNumber); }
}
@Override public long getTimestamp() {
return Math.round(getFrameNumber() * 1000000L / getFrameRate());
}
@Override public void setTimestamp(long timestamp) {
setFrameNumber((int)Math.round(timestamp * getFrameRate() / 1000000L));
}
public void start(AVFormatContext ifmt_ctx) throws Exception {
this.ifmt_ctx = ifmt_ctx;
start();
}
public void start() throws Exception {
int ret;
picture = null;
tmp_picture = null;
picture_buf = null;
frame = null;
video_outbuf = null;
audio_outbuf = null;
oc = new AVFormatContext(null);
video_c = null;
audio_c = null;
video_st = null;
audio_st = null;
got_video_packet = new int[1];
got_audio_packet = new int[1];
String format_name = format == null || format.length() == 0 ? null : format;
if ((oformat = av_guess_format(format_name, filename, null)) == null) {
int proto = filename.indexOf("://");
if (proto > 0) {
format_name = filename.substring(0, proto);
}
if ((oformat = av_guess_format(format_name, filename, null)) == null) {
throw new Exception("av_guess_format() error: Could not guess output format for \"" + filename + "\" and " + format + " format.");
}
}
format_name = oformat.name().getString();
if (avformat_alloc_output_context2(oc, null, format_name, filename) < 0) {
throw new Exception("avformat_alloc_context2() error:\tCould not allocate format context");
}
if (outputStream != null) {
avio = avio_alloc_context(new BytePointer(av_malloc(4096)), 4096, 1, oc, null, writeCallback, null);
oc.pb(avio);
filename = outputStream.toString();
outputStreams.put(oc, outputStream);
}
oc.oformat(oformat);
oc.filename().putString(filename);
oc.max_delay(maxDelay);
AVStream inpVideoStream = null, inpAudioStream = null;
if (ifmt_ctx != null) {
for (int idx = 0; idx < ifmt_ctx.nb_streams(); idx++) {
AVStream inputStream = ifmt_ctx.streams(idx);
if (inputStream.codec().codec_type() == AVMEDIA_TYPE_VIDEO) {
inpVideoStream = inputStream;
videoCodec = inpVideoStream.codec().codec_id();
if (inpVideoStream.r_frame_rate().num() != AV_NOPTS_VALUE && inpVideoStream.r_frame_rate().den() != 0) {
frameRate = (inpVideoStream.r_frame_rate().num()) / (inpVideoStream.r_frame_rate().den());
}
} else if (inputStream.codec().codec_type() == AVMEDIA_TYPE_AUDIO) {
inpAudioStream = inputStream;
audioCodec = inpAudioStream.codec().codec_id();
}
}
}
if (imageWidth > 0 && imageHeight > 0) {
if (videoCodec != AV_CODEC_ID_NONE) {
oformat.video_codec(videoCodec);
} else if ("flv".equals(format_name)) {
oformat.video_codec(AV_CODEC_ID_FLV1);
} else if ("mp4".equals(format_name)) {
oformat.video_codec(AV_CODEC_ID_MPEG4);
} else if ("3gp".equals(format_name)) {
oformat.video_codec(AV_CODEC_ID_H263);
} else if ("avi".equals(format_name)) {
oformat.video_codec(AV_CODEC_ID_HUFFYUV);
}
if ((video_codec = avcodec_find_encoder_by_name(videoCodecName)) == null &&
(video_codec = avcodec_find_encoder(oformat.video_codec())) == null) {
release();
throw new Exception("avcodec_find_encoder() error: Video codec not found.");
}
oformat.video_codec(video_codec.id());
AVRational frame_rate = av_d2q(frameRate, 1001000);
AVRational supported_framerates = video_codec.supported_framerates();
if (supported_framerates != null) {
int idx = av_find_nearest_q_idx(frame_rate, supported_framerates);
frame_rate = supported_framerates.position(idx);
}
if ((video_st = avformat_new_stream(oc, null)) == null) {
release();
throw new Exception("avformat_new_stream() error: Could not allocate video stream.");
}
if ((video_c = avcodec_alloc_context3(video_codec)) == null) {
release();
throw new Exception("avcodec_alloc_context3() error: Could not allocate video encoding context.");
}
if (inpVideoStream != null) {
if ((ret = avcodec_copy_context(video_st.codec(), inpVideoStream.codec())) < 0) {
release();
throw new Exception("avcodec_copy_context() error:\tFailed to copy context from input to output stream codec context");
}
videoBitrate = (int) inpVideoStream.codec().bit_rate();
pixelFormat = inpVideoStream.codec().pix_fmt();
aspectRatio = inpVideoStream.codec().sample_aspect_ratio().den() / inpVideoStream.codec().sample_aspect_ratio().den() * 1.d;
videoQuality = inpVideoStream.codec().global_quality();
video_c.codec_tag(0);
}
video_c.codec_id(oformat.video_codec());
video_c.codec_type(AVMEDIA_TYPE_VIDEO);
video_c.bit_rate(videoBitrate);
if (imageWidth % 2 == 1) {
int roundedWidth = imageWidth + 1;
imageHeight = (roundedWidth * imageHeight + imageWidth / 2) / imageWidth;
imageWidth = roundedWidth;
}
video_c.width(imageWidth);
video_c.height(imageHeight);
if (aspectRatio > 0) {
AVRational r = av_d2q(aspectRatio, 255);
video_c.sample_aspect_ratio(r);
video_st.sample_aspect_ratio(r);
}
video_c.time_base(av_inv_q(frame_rate));
video_st.time_base(av_inv_q(frame_rate));
if (gopSize >= 0) {
video_c.gop_size(gopSize);
}
if (videoQuality >= 0) {
video_c.flags(video_c.flags() | AV_CODEC_FLAG_QSCALE);
video_c.global_quality((int)Math.round(FF_QP2LAMBDA * videoQuality));
}
if (pixelFormat != AV_PIX_FMT_NONE) {
video_c.pix_fmt(pixelFormat);
} else if (video_c.codec_id() == AV_CODEC_ID_RAWVIDEO || video_c.codec_id() == AV_CODEC_ID_PNG ||
video_c.codec_id() == AV_CODEC_ID_HUFFYUV || video_c.codec_id() == AV_CODEC_ID_FFV1) {
video_c.pix_fmt(AV_PIX_FMT_RGB32);
} else if (video_c.codec_id() == AV_CODEC_ID_JPEGLS) {
video_c.pix_fmt(AV_PIX_FMT_BGR24);
} else if (video_c.codec_id() == AV_CODEC_ID_MJPEG || video_c.codec_id() == AV_CODEC_ID_MJPEGB) {
video_c.pix_fmt(AV_PIX_FMT_YUVJ420P);
} else {
video_c.pix_fmt(AV_PIX_FMT_YUV420P);
}
if (video_c.codec_id() == AV_CODEC_ID_MPEG2VIDEO) {
video_c.max_b_frames(2);
} else if (video_c.codec_id() == AV_CODEC_ID_MPEG1VIDEO) {
video_c.mb_decision(2);
} else if (video_c.codec_id() == AV_CODEC_ID_H263) {
if (imageWidth <= 128 && imageHeight <= 96) {
video_c.width(128).height(96);
} else if (imageWidth <= 176 && imageHeight <= 144) {
video_c.width(176).height(144);
} else if (imageWidth <= 352 && imageHeight <= 288) {
video_c.width(352).height(288);
} else if (imageWidth <= 704 && imageHeight <= 576) {
video_c.width(704).height(576);
} else {
video_c.width(1408).height(1152);
}
} else if (video_c.codec_id() == AV_CODEC_ID_H264) {
video_c.profile(AVCodecContext.FF_PROFILE_H264_CONSTRAINED_BASELINE);
}
if ((oformat.flags() & AVFMT_GLOBALHEADER) != 0) {
video_c.flags(video_c.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
}
if ((video_codec.capabilities() & AV_CODEC_CAP_EXPERIMENTAL) != 0) {
video_c.strict_std_compliance(AVCodecContext.FF_COMPLIANCE_EXPERIMENTAL);
}
if (maxBFrames >= 0) {
video_c.max_b_frames(maxBFrames);
video_c.has_b_frames(maxBFrames == 0 ? 0 : 1);
}
if (trellis >= 0) {
video_c.trellis(trellis);
}
}
if (audioChannels > 0 && audioBitrate > 0 && sampleRate > 0) {
if (audioCodec != AV_CODEC_ID_NONE) {
oformat.audio_codec(audioCodec);
} else if ("flv".equals(format_name) || "mp4".equals(format_name) || "3gp".equals(format_name)) {
oformat.audio_codec(AV_CODEC_ID_AAC);
} else if ("avi".equals(format_name)) {
oformat.audio_codec(AV_CODEC_ID_PCM_S16LE);
}
if ((audio_codec = avcodec_find_encoder_by_name(audioCodecName)) == null &&
(audio_codec = avcodec_find_encoder(oformat.audio_codec())) == null) {
release();
throw new Exception("avcodec_find_encoder() error: Audio codec not found.");
}
oformat.audio_codec(audio_codec.id());
if ((audio_st = avformat_new_stream(oc, null)) == null) {
release();
throw new Exception("avformat_new_stream() error: Could not allocate audio stream.");
}
if ((audio_c = avcodec_alloc_context3(audio_codec)) == null) {
release();
throw new Exception("avcodec_alloc_context3() error: Could not allocate audio encoding context.");
}
if(inpAudioStream != null && audioChannels > 0){
if ((ret = avcodec_copy_context(audio_st.codec(), inpAudioStream.codec())) < 0) {
throw new Exception("avcodec_copy_context() error:\tFailed to copy context from input audio to output audio stream codec context\n");
}
audioBitrate = (int) inpAudioStream.codec().bit_rate();
sampleRate = inpAudioStream.codec().sample_rate();
audioChannels = inpAudioStream.codec().channels();
sampleFormat = inpAudioStream.codec().sample_fmt();
audioQuality = inpAudioStream.codec().global_quality();
audio_c.codec_tag(0);
audio_st.duration(inpAudioStream.duration());
audio_st.time_base().num(inpAudioStream.time_base().num());
audio_st.time_base().den(inpAudioStream.time_base().den());
}
audio_c.codec_id(oformat.audio_codec());
audio_c.codec_type(AVMEDIA_TYPE_AUDIO);
audio_c.bit_rate(audioBitrate);
audio_c.sample_rate(sampleRate);
audio_c.channels(audioChannels);
audio_c.channel_layout(av_get_default_channel_layout(audioChannels));
if (sampleFormat != AV_SAMPLE_FMT_NONE) {
audio_c.sample_fmt(sampleFormat);
} else {
audio_c.sample_fmt(AV_SAMPLE_FMT_FLTP);
IntPointer formats = audio_c.codec().sample_fmts();
for (int i = 0; formats.get(i) != -1; i++) {
if (formats.get(i) == AV_SAMPLE_FMT_S16) {
audio_c.sample_fmt(AV_SAMPLE_FMT_S16);
break;
}
}
}
audio_c.time_base().num(1).den(sampleRate);
audio_st.time_base().num(1).den(sampleRate);
switch (audio_c.sample_fmt()) {
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P: audio_c.bits_per_raw_sample(8); break;
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P: audio_c.bits_per_raw_sample(16); break;
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P: audio_c.bits_per_raw_sample(32); break;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP: audio_c.bits_per_raw_sample(32); break;
case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_DBLP: audio_c.bits_per_raw_sample(64); break;
default: assert false;
}
if (audioQuality >= 0) {
audio_c.flags(audio_c.flags() | AV_CODEC_FLAG_QSCALE);
audio_c.global_quality((int)Math.round(FF_QP2LAMBDA * audioQuality));
}
if ((oformat.flags() & AVFMT_GLOBALHEADER) != 0) {
audio_c.flags(audio_c.flags() | AV_CODEC_FLAG_GLOBAL_HEADER);
}
if ((audio_codec.capabilities() & AV_CODEC_CAP_EXPERIMENTAL) != 0) {
audio_c.strict_std_compliance(AVCodecContext.FF_COMPLIANCE_EXPERIMENTAL);
}
}
if (video_st != null && inpVideoStream == null) {
AVDictionary options = new AVDictionary(null);
if (videoQuality >= 0) {
av_dict_set(options, "crf", "" + videoQuality, 0);
}
for (Entry<String, String> e : videoOptions.entrySet()) {
av_dict_set(options, e.getKey(), e.getValue(), 0);
}
if ((ret = avcodec_open2(video_c, video_codec, options)) < 0) {
release();
av_dict_free(options);
throw new Exception("avcodec_open2() error " + ret + ": Could not open video codec.");
}
av_dict_free(options);
video_outbuf = null;
if ((picture = av_frame_alloc()) == null) {
release();
throw new Exception("av_frame_alloc() error: Could not allocate picture.");
}
picture.pts(0);
int size = av_image_get_buffer_size(video_c.pix_fmt(), video_c.width(), video_c.height(), 1);
if ((picture_buf = new BytePointer(av_malloc(size))).isNull()) {
release();
throw new Exception("av_malloc() error: Could not allocate picture buffer.");
}
if ((tmp_picture = av_frame_alloc()) == null) {
release();
throw new Exception("av_frame_alloc() error: Could not allocate temporary picture.");
}
if ((ret = avcodec_parameters_from_context(video_st.codecpar(), video_c)) < 0) {
release();
throw new Exception("avcodec_parameters_from_context() error: Could not copy the video stream parameters.");
}
AVDictionary metadata = new AVDictionary(null);
for (Entry<String, String> e : videoMetadata.entrySet()) {
av_dict_set(metadata, e.getKey(), e.getValue(), 0);
}
video_st.metadata(metadata);
}
if (audio_st != null && inpAudioStream == null) {
AVDictionary options = new AVDictionary(null);
if (audioQuality >= 0) {
av_dict_set(options, "crf", "" + audioQuality, 0);
}
for (Entry<String, String> e : audioOptions.entrySet()) {
av_dict_set(options, e.getKey(), e.getValue(), 0);
}
if ((ret = avcodec_open2(audio_c, audio_codec, options)) < 0) {
release();
av_dict_free(options);
throw new Exception("avcodec_open2() error " + ret + ": Could not open audio codec.");
}
av_dict_free(options);
audio_outbuf_size = 256 * 1024;
audio_outbuf = new BytePointer(av_malloc(audio_outbuf_size));
if (audio_c.frame_size() <= 1) {
audio_outbuf_size = AV_INPUT_BUFFER_MIN_SIZE;
audio_input_frame_size = audio_outbuf_size / audio_c.channels();
switch (audio_c.codec_id()) {
case AV_CODEC_ID_PCM_S16LE:
case AV_CODEC_ID_PCM_S16BE:
case AV_CODEC_ID_PCM_U16LE:
case AV_CODEC_ID_PCM_U16BE:
audio_input_frame_size >>= 1;
break;
default:
break;
}
} else {
audio_input_frame_size = audio_c.frame_size();
}
int planes = av_sample_fmt_is_planar(audio_c.sample_fmt()) != 0 ? (int)audio_c.channels() : 1;
int data_size = av_samples_get_buffer_size((IntPointer)null, audio_c.channels(),
audio_input_frame_size, audio_c.sample_fmt(), 1) / planes;
samples_out = new BytePointer[planes];
for (int i = 0; i < samples_out.length; i++) {
samples_out[i] = new BytePointer(av_malloc(data_size)).capacity(data_size);
}
samples_in = new Pointer[AVFrame.AV_NUM_DATA_POINTERS];
samples_in_ptr = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS);
samples_out_ptr = new PointerPointer(AVFrame.AV_NUM_DATA_POINTERS);
if ((frame = av_frame_alloc()) == null) {
release();
throw new Exception("av_frame_alloc() error: Could not allocate audio frame.");
}
frame.pts(0);
if ((ret = avcodec_parameters_from_context(audio_st.codecpar(), audio_c)) < 0) {
release();
throw new Exception("avcodec_parameters_from_context() error: Could not copy the audio stream parameters.");
}
AVDictionary metadata = new AVDictionary(null);
for (Entry<String, String> e : audioMetadata.entrySet()) {
av_dict_set(metadata, e.getKey(), e.getValue(), 0);
}
audio_st.metadata(metadata);
}
AVDictionary options = new AVDictionary(null);
for (Entry<String, String> e : this.options.entrySet()) {
av_dict_set(options, e.getKey(), e.getValue(), 0);
}
if (outputStream == null && (oformat.flags() & AVFMT_NOFILE) == 0) {
AVIOContext pb = new AVIOContext(null);
if ((ret = avio_open2(pb, filename, AVIO_FLAG_WRITE, null, options)) < 0) {
release();
av_dict_free(options);
throw new Exception("avio_open2 error() error " + ret + ": Could not open '" + filename + "'");
}
oc.pb(pb);
}
AVDictionary metadata = new AVDictionary(null);
for (Entry<String, String> e : this.metadata.entrySet()) {
av_dict_set(metadata, e.getKey(), e.getValue(), 0);
}
avformat_write_header(oc.metadata(metadata), options);
av_dict_free(options);
if (av_log_get_level() >= AV_LOG_INFO) {
av_dump_format(oc, 0, filename, 1);
}
}
public void stop() throws Exception {
if (oc != null) {
try {
while (video_st != null && ifmt_ctx == null && recordImage(0, 0, 0, 0, 0, AV_PIX_FMT_NONE, (Buffer[])null));
while (audio_st != null && ifmt_ctx == null && recordSamples(0, 0, (Buffer[])null));
if (interleaved && video_st != null && audio_st != null) {
av_interleaved_write_frame(oc, null);
} else {
av_write_frame(oc, null);
}
av_write_trailer(oc);
} finally {
release();
}
}
}
@Override public void record(Frame frame) throws Exception {
record(frame, AV_PIX_FMT_NONE);
}
public void record(Frame frame, int pixelFormat) throws Exception {
if (frame == null || (frame.image == null && frame.samples == null)) {
recordImage(0, 0, 0, 0, 0, pixelFormat, (Buffer[])null);
} else {
if (frame.image != null) {
frame.keyFrame = recordImage(frame.imageWidth, frame.imageHeight, frame.imageDepth,
frame.imageChannels, frame.imageStride, pixelFormat, frame.image);
}
if (frame.samples != null&&audio_st != null) {
frame.keyFrame = recordSamples(frame.sampleRate, frame.audioChannels, frame.samples);
}
}
}
public boolean recordImage(int width, int height, int depth, int channels, int stride, int pixelFormat, Buffer ... image) throws Exception {
if (video_st == null) {
throw new Exception("No video output stream (Is imageWidth > 0 && imageHeight > 0 and has start() been called?)");
}
int ret;
if (image == null || image.length == 0) {
} else {
int step = stride * Math.abs(depth) / 8;
BytePointer data = image[0] instanceof ByteBuffer
? new BytePointer((ByteBuffer)image[0].position(0))
: new BytePointer(new Pointer(image[0].position(0)));
if (pixelFormat == AV_PIX_FMT_NONE) {
if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 3) {
pixelFormat = AV_PIX_FMT_BGR24;
} else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 1) {
pixelFormat = AV_PIX_FMT_GRAY8;
} else if ((depth == Frame.DEPTH_USHORT || depth == Frame.DEPTH_SHORT) && channels == 1) {
pixelFormat = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) ?
AV_PIX_FMT_GRAY16BE : AV_PIX_FMT_GRAY16LE;
} else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 4) {
pixelFormat = AV_PIX_FMT_RGBA;
} else if ((depth == Frame.DEPTH_UBYTE || depth == Frame.DEPTH_BYTE) && channels == 2) {
pixelFormat = AV_PIX_FMT_NV21;
} else {
throw new Exception("Could not guess pixel format of image: depth=" + depth + ", channels=" + channels);
}
}
if (pixelFormat == AV_PIX_FMT_NV21) {
step = width;
}
if (video_c.pix_fmt() != pixelFormat || video_c.width() != width || video_c.height() != height) {
img_convert_ctx = sws_getCachedContext(img_convert_ctx, width, height, pixelFormat,
video_c.width(), video_c.height(), video_c.pix_fmt(), SWS_BILINEAR,
null, null, (DoublePointer)null);
if (img_convert_ctx == null) {
throw new Exception("sws_getCachedContext() error: Cannot initialize the conversion context.");
}
av_image_fill_arrays(new PointerPointer(tmp_picture), tmp_picture.linesize(), data, pixelFormat, width, height, 1);
av_image_fill_arrays(new PointerPointer(picture), picture.linesize(), picture_buf, video_c.pix_fmt(), video_c.width(), video_c.height(), 1);
tmp_picture.linesize(0, step);
tmp_picture.format(pixelFormat);
tmp_picture.width(width);
tmp_picture.height(height);
picture.format(video_c.pix_fmt());
picture.width(video_c.width());
picture.height(video_c.height());
sws_scale(img_convert_ctx, new PointerPointer(tmp_picture), tmp_picture.linesize(),
0, height, new PointerPointer(picture), picture.linesize());
} else {
av_image_fill_arrays(new PointerPointer(picture), picture.linesize(), data, pixelFormat, width, height, 1);
picture.linesize(0, step);
picture.format(pixelFormat);
picture.width(width);
picture.height(height);
}
}
av_init_packet(video_pkt);
video_pkt.data(video_outbuf);
video_pkt.size(video_outbuf_size);
picture.quality(video_c.global_quality());
if ((ret = avcodec_encode_video2(video_c, video_pkt, image == null || image.length == 0 ? null : picture, got_video_packet)) < 0) {
throw new Exception("avcodec_encode_video2() error " + ret + ": Could not encode video packet.");
}
picture.pts(picture.pts() + 1);
if (got_video_packet[0] != 0) {
if (video_pkt.pts() != AV_NOPTS_VALUE) {
video_pkt.pts(av_rescale_q(video_pkt.pts(), video_c.time_base(), video_st.time_base()));
}
if (video_pkt.dts() != AV_NOPTS_VALUE) {
video_pkt.dts(av_rescale_q(video_pkt.dts(), video_c.time_base(), video_st.time_base()));
}
video_pkt.stream_index(video_st.index());
} else {
return false;
}
writePacket(AVMEDIA_TYPE_VIDEO, video_pkt);
return image != null ? (video_pkt.flags() & AV_PKT_FLAG_KEY) != 0 : got_video_packet[0] != 0;
}
public boolean recordSamples(Buffer ... samples) throws Exception {
return recordSamples(0, 0, samples);
}
public boolean recordSamples(int sampleRate, int audioChannels, Buffer ... samples) throws Exception {
if (audio_st == null) {
throw new Exception("No audio output stream (Is audioChannels > 0 and has start() been called?)");
}
if (samples == null && samples_out[0].position() > 0) {
double sampleDivisor = Math.floor((int)Math.min(samples_out[0].limit(), Integer.MAX_VALUE) / audio_input_frame_size);
writeSamples((int)Math.floor((int)samples_out[0].position() / sampleDivisor));
return record((AVFrame)null);
}
int ret;
if (sampleRate <= 0) {
sampleRate = audio_c.sample_rate();
}
if (audioChannels <= 0) {
audioChannels = audio_c.channels();
}
int inputSize = samples != null ? samples[0].limit() - samples[0].position() : 0;
int inputFormat = samples_format;
int inputChannels = samples != null && samples.length > 1 ? 1 : audioChannels;
int inputDepth = 0;
int outputFormat = audio_c.sample_fmt();
int outputChannels = samples_out.length > 1 ? 1 : audio_c.channels();
int outputDepth = av_get_bytes_per_sample(outputFormat);
if (samples != null && samples[0] instanceof ByteBuffer) {
inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_U8P : AV_SAMPLE_FMT_U8;
inputDepth = 1;
for (int i = 0; i < samples.length; i++) {
ByteBuffer b = (ByteBuffer)samples[i];
if (samples_in[i] instanceof BytePointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {
((BytePointer)samples_in[i]).position(0).put(b.array(), b.position(), inputSize);
} else {
samples_in[i] = new BytePointer(b);
}
}
} else if (samples != null && samples[0] instanceof ShortBuffer) {
inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16;
inputDepth = 2;
for (int i = 0; i < samples.length; i++) {
ShortBuffer b = (ShortBuffer)samples[i];
if (samples_in[i] instanceof ShortPointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {
((ShortPointer)samples_in[i]).position(0).put(b.array(), samples[i].position(), inputSize);
} else {
samples_in[i] = new ShortPointer(b);
}
}
} else if (samples != null && samples[0] instanceof IntBuffer) {
inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_S32P : AV_SAMPLE_FMT_S32;
inputDepth = 4;
for (int i = 0; i < samples.length; i++) {
IntBuffer b = (IntBuffer)samples[i];
if (samples_in[i] instanceof IntPointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {
((IntPointer)samples_in[i]).position(0).put(b.array(), samples[i].position(), inputSize);
} else {
samples_in[i] = new IntPointer(b);
}
}
} else if (samples != null && samples[0] instanceof FloatBuffer) {
inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT;
inputDepth = 4;
for (int i = 0; i < samples.length; i++) {
FloatBuffer b = (FloatBuffer)samples[i];
if (samples_in[i] instanceof FloatPointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {
((FloatPointer)samples_in[i]).position(0).put(b.array(), b.position(), inputSize);
} else {
samples_in[i] = new FloatPointer(b);
}
}
} else if (samples != null && samples[0] instanceof DoubleBuffer) {
inputFormat = samples.length > 1 ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL;
inputDepth = 8;
for (int i = 0; i < samples.length; i++) {
DoubleBuffer b = (DoubleBuffer)samples[i];
if (samples_in[i] instanceof DoublePointer && samples_in[i].capacity() >= inputSize && b.hasArray()) {
((DoublePointer)samples_in[i]).position(0).put(b.array(), b.position(), inputSize);
} else {
samples_in[i] = new DoublePointer(b);
}
}
} else if (samples != null) {
throw new Exception("Audio samples Buffer has unsupported type: " + samples);
}
if (samples_convert_ctx == null || samples_channels != audioChannels || samples_format != inputFormat || samples_rate != sampleRate) {
samples_convert_ctx = swr_alloc_set_opts(samples_convert_ctx, audio_c.channel_layout(), outputFormat, audio_c.sample_rate(),
av_get_default_channel_layout(audioChannels), inputFormat, sampleRate, 0, null);
if (samples_convert_ctx == null) {
throw new Exception("swr_alloc_set_opts() error: Cannot allocate the conversion context.");
} else if ((ret = swr_init(samples_convert_ctx)) < 0) {
throw new Exception("swr_init() error " + ret + ": Cannot initialize the conversion context.");
}
samples_channels = audioChannels;
samples_format = inputFormat;
samples_rate = sampleRate;
}
for (int i = 0; samples != null && i < samples.length; i++) {
samples_in[i].position(samples_in[i].position() * inputDepth).
limit((samples_in[i].position() + inputSize) * inputDepth);
}
while (true) {
int inputCount = (int)Math.min(samples != null ? (samples_in[0].limit() - samples_in[0].position()) / (inputChannels * inputDepth) : 0, Integer.MAX_VALUE);
int outputCount = (int)Math.min((samples_out[0].limit() - samples_out[0].position()) / (outputChannels * outputDepth), Integer.MAX_VALUE);
inputCount = Math.min(inputCount, (outputCount * sampleRate + audio_c.sample_rate() - 1) / audio_c.sample_rate());
for (int i = 0; samples != null && i < samples.length; i++) {
samples_in_ptr.put(i, samples_in[i]);
}
for (int i = 0; i < samples_out.length; i++) {
samples_out_ptr.put(i, samples_out[i]);
}
if ((ret = swr_convert(samples_convert_ctx, samples_out_ptr, outputCount, samples_in_ptr, inputCount)) < 0) {
throw new Exception("swr_convert() error " + ret + ": Cannot convert audio samples.");
} else if (ret == 0) {
break;
}
for (int i = 0; samples != null && i < samples.length; i++) {
samples_in[i].position(samples_in[i].position() + inputCount * inputChannels * inputDepth);
}
for (int i = 0; i < samples_out.length; i++) {
samples_out[i].position(samples_out[i].position() + ret * outputChannels * outputDepth);
}
if (samples == null || samples_out[0].position() >= samples_out[0].limit()) {
writeSamples(audio_input_frame_size);
}
}
return samples != null ? frame.key_frame() != 0 : record((AVFrame)null);
}
private void writeSamples(int nb_samples) throws Exception {
if (samples_out == null || samples_out.length == 0) {
return;
}
frame.nb_samples(nb_samples);
avcodec_fill_audio_frame(frame, audio_c.channels(), audio_c.sample_fmt(), samples_out[0], (int)samples_out[0].position(), 0);
for (int i = 0; i < samples_out.length; i++) {
int linesize = 0;
if (samples_out[0].position() > 0 && samples_out[0].position() < samples_out[0].limit()) {
linesize = (int)samples_out[i].position();
} else {
linesize = (int)Math.min(samples_out[i].limit(), Integer.MAX_VALUE);
}
frame.data(i, samples_out[i].position(0));
frame.linesize(i, linesize);
}
frame.quality(audio_c.global_quality());
record(frame);
}
boolean record(AVFrame frame) throws Exception {
int ret;
av_init_packet(audio_pkt);
audio_pkt.data(audio_outbuf);
audio_pkt.size(audio_outbuf_size);
if ((ret = avcodec_encode_audio2(audio_c, audio_pkt, frame, got_audio_packet)) < 0) {
throw new Exception("avcodec_encode_audio2() error " + ret + ": Could not encode audio packet.");
}
if (frame != null) {
frame.pts(frame.pts() + frame.nb_samples());
}
if (got_audio_packet[0] != 0) {
if (audio_pkt.pts() != AV_NOPTS_VALUE) {
audio_pkt.pts(av_rescale_q(audio_pkt.pts(), audio_c.time_base(), audio_st.time_base()));
}
if (audio_pkt.dts() != AV_NOPTS_VALUE) {
audio_pkt.dts(av_rescale_q(audio_pkt.dts(), audio_c.time_base(), audio_st.time_base()));
}
audio_pkt.flags(audio_pkt.flags() | AV_PKT_FLAG_KEY);
audio_pkt.stream_index(audio_st.index());
} else {
return false;
}
writePacket(AVMEDIA_TYPE_AUDIO, audio_pkt);
return true;
}
private boolean writePacket(int mediaType, AVPacket avPacket) throws Exception {
AVStream avStream = (mediaType == AVMEDIA_TYPE_VIDEO) ? audio_st : (mediaType == AVMEDIA_TYPE_AUDIO) ? video_st : null;
String mediaTypeStr = (mediaType == AVMEDIA_TYPE_VIDEO) ? "video" : (mediaType == AVMEDIA_TYPE_AUDIO) ? "audio" : "unsupported media stream type";
if (interleaved && avStream != null) {
if (av_interleaved_write_frame(oc, avPacket) < 0) {
return false;
}
} else {
if (av_write_frame(oc, avPacket) < 0) {
return false;
}
}
return true;
}
public boolean recordPacket(AVPacket pkt) throws Exception {
if (pkt == null) {
return false;
}
AVStream in_stream = ifmt_ctx.streams(pkt.stream_index());
pkt.pts(AV_NOPTS_VALUE);
pkt.pos(-1);
try {
if (in_stream.codec().codec_type() == AVMEDIA_TYPE_VIDEO && video_st != null) {
pkt.stream_index(video_st.index());
pkt.duration((int) av_rescale_q(pkt.duration(), in_stream.codec().time_base(), video_st.codec().time_base()));
pkt.dts(av_rescale_q_rnd(pkt.dts(), in_stream.time_base(), video_st.time_base(),(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)));
return writePacket(AVMEDIA_TYPE_VIDEO, pkt);
} else if (in_stream.codec().codec_type() == AVMEDIA_TYPE_AUDIO && audio_st != null && (audioChannels > 0)) {
pkt.stream_index(audio_st.index());
pkt.duration((int) av_rescale_q(pkt.duration(), in_stream.codec().time_base(), audio_st.codec().time_base()));
pkt.dts(av_rescale_q_rnd(pkt.dts(), in_stream.time_base(), audio_st.time_base(),(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)));
return writePacket(AVMEDIA_TYPE_AUDIO, pkt);
}
}finally {
avcodec.av_packet_unref(pkt);
av_freep(pkt);
}
return true;
}
}