package com.zj.thread;
import java.io.*;
import java.util.*;
import com.zj.common.MediaConstant;
import com.zj.dto.CameraDto;
import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import org.opencv.video.Video;
import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;
/**
* hls切片
* @author ZJ
*
*/
@Slf4j
public class MediaTransferHls extends MediaTransfer {
/**
* 运行状态
*/
private boolean running = false;
private boolean enableLog = false;
private Process process;
private Thread inputThread;
private Thread errThread;
private int port = 8888;
/**
* 相机
*/
private CameraDto cameraDto;
/**
* cmd
*/
private List<String> command = new ArrayList<>();
public boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
/**
*
* @param cameraDto
*/
public MediaTransferHls(CameraDto cameraDto, int port) {
this.cameraDto = cameraDto;
this.port = port;
buildCommand();
}
/**
* String cmd = "ffmpeg -i rtsp://admin:VZCDOY@192.168.2.120:554/Streaming/Channels/102 -r 25 -g 25 -c:v libx264 -c:a aac -f hls -hls_list_size 1 -hls_wrap 6 -hls_time 1 -hls_base_url /ts/"+22+"/ -method put http://localhost:8888/record/"+22+"/out.m3u8";
*/
private void buildCommand() {
command.add(System.getProperty(MediaConstant.ffmpegPathKey));
command.add("-i");
command.add(cameraDto.getUrl());
command.add("-r");
command.add("25");
command.add("-g");
command.add("25");
command.add("-c:v");
command.add("libopenh264"); //javacv 1.5.5 无法使用libx264
command.add("-c:a");
command.add("aac");
command.add("-f");
command.add("hls");
command.add("-hls_list_size");
command.add("1");
command.add("-hls_wrap");
command.add("6");
command.add("-hls_time");
command.add("1");
command.add("-hls_base_url");
command.add("/ts/"+cameraDto.getMediaKey()+"/");
command.add("-method");
command.add("put");
command.add("http://localhost:"+port+"/record/"+cameraDto.getMediaKey()+"/out.m3u8");
}
/**
* 执行
*/
public void execute() {
String join = CollUtil.join(command, " ");
log.info(join);
try {
process = new ProcessBuilder(command).start();
running = true;
dealStream(process);
} catch (IOException e) {
running = false;
e.printStackTrace();
}
}
/**
* 关闭
*/
public void stop() {
this.running = false;
try {
process.destroy();
log.info("关闭媒体流-ffmpeg,{} ", cameraDto.getUrl());
} catch (Exception e) {
process.destroyForcibly();
}
}
/**
* 控制台输出
*
* @param process
*/
private void dealStream(Process process) {
if (process == null) {
return;
}
// 处理InputStream的线程
inputThread = new Thread() {
@Override
public void run() {
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
try {
while (running) {
line = in.readLine();
if (line == null) {
break;
}
if (enableLog) {
log.info("output: " + line);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
running = false;
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
// 处理ErrorStream的线程
errThread = new Thread() {
@Override
public void run() {
BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = null;
try {
while (running) {
line = err.readLine();
if (line == null) {
break;
}
if (enableLog) {
log.info("err: " + line);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
running = false;
err.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
inputThread.start();
errThread.start();
}
public static void main(String[] args) throws IOException {
String input = "video/file.mp4";
//convertMediaToM3u8ByHttp(input);
toMp3(input);
String output = "ts/file.m3u8";
// push(input,output);
}
public static void convertMediaToM3u8ByHttp(String filePath) throws IOException {
avutil.av_log_set_level(avutil.AV_LOG_INFO);
FFmpegLogCallback.set();
String m3u8Url = "ts/file.m3u8";
String infoUrl = "video/file.info";
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(filePath);
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream);
grabber.start();
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(m3u8Url, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
recorder.setFormat("hls");
recorder.setOption("hls_time", "10");
recorder.setOption("hls_list_size", "0");
recorder.setOption("hls_flags", "delete_segments");
recorder.setOption("hls_delete_threshold", "1");
recorder.setOption("hls_segment_type", "mpegts");
recorder.setOption("hls_segment_filename", "ts/file-%d.ts");
// recorder.setOption("hls_key_info_file", infoUrl);
// http属性
// recorder.setOption("method", "POST");
recorder.setAudioCodec(grabber.getAudioCodec());
Map<String, String> audioOptions = grabber.getAudioOptions();
recorder.setAudioOptions(audioOptions);
Map<String, String> audioMetadata = grabber.getAudioMetadata();
recorder.setFrameRate(grabber.getFrameRate());
// recorder.setGopSize(2 * 25);
recorder.setVideoQuality(1.0);
recorder.setVideoBitrate(1024 * 1024);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
// recorder.setAudioBitrate(512 * 1024);
recorder.start();
Frame frame;
while ((frame = grabber.grab()) != null) {
try {
EnumSet<Frame.Type> types = frame.getTypes();
recorder.record(frame);
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
}
}
recorder.setTimestamp(grabber.getTimestamp());
recorder.close();
grabber.close();
}
public static void toMp3(String filePath){
System.out.println("提取音频文件");
File file=new File(filePath);
//抓取资源
FFmpegFrameGrabber frameGrabber1 = new FFmpegFrameGrabber(filePath);
Frame frame = null;
FFmpegFrameRecorder recorder = null;
String fileName = null;
try{
frameGrabber1.start();
//
//随机数
Random random=new Random();
fileName = file.getAbsolutePath() + random.nextInt(100)+".mp3";
System.out.println("--文件名-->>"+fileName);
recorder = new FFmpegFrameRecorder(fileName,frameGrabber1.getAudioChannels());
recorder.setFormat("mp3");
recorder.setSampleRate(frameGrabber1.getSampleRate());
recorder.setTimestamp(frameGrabber1.getTimestamp());
recorder.setAudioQuality(0);
recorder.start();
int index=0;
while (true){
frame=frameGrabber1.grab();
if(frame==null){
System.out.println("视频处理完成");
break;
}
if(frame.samples!=null){
recorder.recordSamples(frame.sampleRate,frame.audioChannels,frame.samples);
}
System.out.println("帧值="+index);
index ++;
}
recorder.stop();
recorder.release();
frameGrabber1.stop();
}catch (Exception e){
e.printStackTrace();
}
}
public static void push(String input, String output)
throws org.bytedeco.javacv.FrameGrabber.Exception, org.bytedeco.javacv.FrameRecorder.Exception {
FFmpegFrameGrabber grabber = null;// 采集器
FFmpegFrameRecorder recorder = null;// 解码器
int bitrate = 2500000;// 比特率
double framerate;// 帧率
int err_index = 0;// 推流过程中出现错误的次数
int timebase;// 时钟基
long dts = 0, pts = 0;// pkt的dts、pts时间戳
try {
// 开启ffmpeg日志级别;QUIET是屏蔽所有,可选INFO、DEBUG、ERROR等
avutil.av_log_set_level(avutil.AV_LOG_INFO);
FFmpegLogCallback.set();
grabber = new FFmpegFrameGrabber(input);
grabber.start();
// 异常的framerate,强制使用25帧
if (grabber.getFrameRate() > 0 && grabber.getFrameRate() < 100) {
framerate = grabber.getFrameRate();
} else {
framerate = 25.0;
}
bitrate = grabber.getVideoBitrate();// 获取到的比特率 0
recorder = new FFmpegFrameRecorder(output, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
// 设置比特率
recorder.setVideoBitrate(bitrate);
// h264编/解码器
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
// 设置音频编码
recorder.setAudioCodec(grabber.getAudioCodec());
recorder.setAudioOptions(grabber.getAudioOptions());
// 视频帧率(保证视频质量的情况下最低25,低于25会出现闪屏)
recorder.setFrameRate(framerate);
// 关键帧间隔,一般与帧率相同或者是视频帧率的两倍
recorder.setGopSize((int) framerate);
// 解码器格式
recorder.setFormat("hls");
// 单个切片时长,单位是s,默认为2s
recorder.setOption("hls_time", "5");
// HLS播放的列表长度,0标识不做限制
recorder.setOption("hls_list_size", "0");
// 设置切片的ts文件序号起始值,默认从0开始,可以通过此项更改
recorder.setOption("start_number", "0");
// recorder.setOption("hls_segment_type","mpegts");
// 自动删除切片,如果切片数量大于hls_list_size的数量,则会开始自动删除之前的ts切片,只保 留hls_list_size个数量的切片
// recorder.setOption("hls_flags","delete_segments");
// ts切片自动删除阈值,默认值为1,表示早于hls_list_size+1的切片将被删除
// recorder.setOption("hls_delete_threshold","1");
/*
* hls的切片类型: 'mpegts':以MPEG-2传输流格式输出ts切片文件,可以与所有HLS版本兼容。 'fmp4':以Fragmented
* MP4(简称:fmp4)格式输出切片文件,类似于MPEG-DASH,fmp4文件可用于HLS version 7和更高版本。
*/
// recorder.setOption("hls_segment_type","mpegts");
AVFormatContext fc = null;
fc = grabber.getFormatContext();
recorder.start(fc);
// 清空探测时留下的缓存
// grabber.flush();
AVPacket pkt = null;
for (int no_pkt_index = 0; no_pkt_index < 5 && err_index < 5;) {
pkt = grabber.grabPacket();
if (pkt == null || pkt.size() <= 0 || pkt.data() == null) {
Thread.sleep(1);
no_pkt_index++;
continue;
}
// 获取到的pkt的dts,pts异常,将此包丢弃掉。
if (pkt.dts() == avutil.AV_NOPTS_VALUE && pkt.pts() == avutil.AV_NOPTS_VALUE || pkt.pts() < dts) {
err_index++;
av_packet_unref(pkt);
continue;
}
// 矫正dts,pts
pkt.pts(pts);
pkt.dts(dts);
err_index += (recorder.recordPacket(pkt) ? 0 : 1);
// pts,dts累加
timebase = grabber.getFormatContext().streams(pkt.stream_index()).time_base().den();
pts += (timebase / (int) framerate);
dts += (timebase / (int) framerate);
}
} catch (Exception e) {
grabber.stop();
grabber.close();
if (recorder != null) {
recorder.stop();
recorder.release();
}
} finally {
grabber.stop();
grabber.close();
if (recorder != null) {
recorder.stop();
recorder.release();
}
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理