一、前言

大家好,俗话说的好,学习新的知识后要学以致用,在学习音视频的过程中,你有没有疑问,不知道音视频可以用来做什么。下面举几个例子,比较耳熟能详,被吹到风口的一些场景有:AI 视觉计算, AI 人脸识别. 细化到一些小的领域,如现在直播技术,摄像头监控拉流;其他还有抖音中的美颜,滤镜,其背后是使用的音视频领域的数字化妆技术。由此可见,音视频技术应用已经应用于我们生活的方方面面。

二、开发背景

想写这篇文章的目的是因为,我有个朋友平时喜欢刷抖音,就经常有一些视频被作者设置成了不可下载保存,朋友下次想再看的话就找不到了。 还有朋友想下载暗恋的妹纸的作品。所以就把苦闷告诉我了,作为朋友当然有义务帮助他走出困境啦,终于,Two thousand years later 的今天,这个小工具终于问世,因为时间原因,来不及写前端页面了,后面有需要的同学可以关注或者私信我,我们一起学习,另外,写本文的目的纯粹是以学习为主,如不小心被不法分子滥用以盈利为目的,与本人无关,请广大道友积极爱护学习环境,记得不要连累我。

三、核心思想

其实核心步骤就两步

1、根据抖音上复制的分享链接获取到抖音的真实地址,需要使用网络编程技术解析到视频的真实地址。

2、然后使用 ffmpeg 解码网络视频流,保存到本地。

四、主要技术点

1、主要使用 Java 与 一些网络调用的知识,例如 Restemplate 的使用,Restemplate 是 spring web 中的一个模板方法类,这里主要用到了他的两个方法(headForHeaders, exchange),当然也可以用其他的工具类或者自己去实现网络远程调用。

2、JSON 解析使用 fastjson,版本号随意,一般都可以兼容。

3、StringUtils 是使用 commons.lang3 包下面中的工具类,不要导错包啦。

4、ffmpeg 拉流使用的第三方依赖是 javacv,版本 1.4.3 版本。如需具体引用依赖,关注或者私信我。

5、如果你对于 ffmpeg 基本概念,音视频基本概念,如视频帧, 音频帧,码率等基本知识不是非常清楚,这里我只说技术的应用,关于原理的讲解,不做过多赘述,网上一搜一大堆,有兴趣可以自己去了解以下。

6、使用 javacv 中的 FFmpegFrameGrabber 帧抓取器来获取音/视频帧,用这个抓取器,可以省略原生的 API 调用的一堆复杂操作,例如打开视频流,查找解码器,判断音频帧和视频帧。

来自网上的一段介绍/概括

FFmpegFrameGrabber 用于采集/抓取视频图像和音频采样。封装了检索流信息,自动猜测视频解码格式,音视频解码等具体 API,并把解码完的像素数据(可配置像素格式)或音频数据保存到 Frame 中返回等功能。

7、还可以使用 ffmpeg 命令行的方式进行下载。命令如下:

ffmpeg -i https://xxx.mp4 -c copy -f flv 艾北.flv

但是这种方式需要部署机安装 ffmpeg,所以暂时不考虑这种方式了。

8、使用 javacv 中的 FrameRecorder 录制器来把已经解码的图像像素编码成对应的编码和格式推流出去,这里保存到本地就是推流到本地文件。

以下是音视频大佬 eguid 对于 FrameRecorder 的介绍概括

FrameRecorder 用于音视频/图片的封装、编码、推流和录制保存等操作。把从 FrameGrabber 或者 FrameFilter 获取的 Frame 中的数据取出并进行编码、封装、推流发送等操作流程。为了方便理解和阅读,下文开始我们统一把 FrameRecorder 简称为:录制器。

五、详细思路

1、链接解析 &接口解析

(1)、Java 正则表达式从字符串中提取出 url。

(2)、使用 RestTemplate.headForHeaders() 方法获取某个资源的 URI 的请求头信息,并且只专注于获取 HTTP 请求头信息。

(3)、第一步中提取出的 url 在浏览器中模拟可以发现, 会重定向到一个新的地址,从请求头中获取重定向后的地址, 即从 header 中获取 location,然后从 location 中获取视频的真实 id。

(4)、根据视频真实 id 和抖音的接口去获取视频信息,如播放信息,作者信息,背景音乐信息等等,使用 json 一层一层解析出来播放地址的 url。

2、ffmpeg 拉流并保存

(1)、使用 ffmpeg 获取 url 视频帧的第一帧,检测视频是否是空视频。

(2)、创建视频流录制器,设置视频参数,分辨率,格式,输出位置。

(3)、循环获取视频帧,使用录制器 recorder 逐帧录制视频帧。

六、核心代码

1、使用正则提取 url
    /**
* 正则表达式提取 url
* @param text
* @return
*/
public static String pickURI(String text) {
// eg: text = "5.1 GV:/ 一出场就给人一种江南的感觉%刘亦菲 %精彩片段 %歌曲红马 https://v.douyin.com/e614JkV/ 腹制佌lian接,打开Dou音搜索,直接观kan视頻!";
Pattern pattern = Pattern.compile("https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]");
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
return matcher.group();
}
return "";
}
 
2、发起网络调用,解析 json 获取真实地址
    public final static String DOU_YIN_WEB_API = "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=";

    /**
* 根据赋值的分享码下载抖音视频
* @param text
* @throws FrameGrabber.Exception
* @throws FrameRecorder.Exception
*/
public static String douYin(String text) throws FrameGrabber.Exception, FrameRecorder.Exception {
//
String url = pickURI(text);
RestTemplate client = new RestTemplate();
//
HttpHeaders headers = client.headForHeaders(url);
String location = headers.getLocation().toString();
String vid = StringUtils.substringBetween(location, "/video/", "/?"); RestTemplate restTemplate = new RestTemplate();
HttpHeaders queryHeaders = new HttpHeaders();
queryHeaders.set(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36");
HttpEntity<String> entity = new HttpEntity<>(queryHeaders);
ResponseEntity<JSONObject> response = restTemplate.exchange(DOU_YIN_WEB_API + vid, HttpMethod.GET, entity, JSONObject.class); if(response.getStatusCodeValue() != 200) {
return "";
}
JSONObject body = response.getBody();
assert body != null;
List<JSONObject> list = JSONArray.parseObject(body.getJSONArray("item_list").toJSONString(), new TypeReference<List<JSONObject>>(){});
if(list.size() == 0) {
return "";
}
JSONObject item = list.get(0);
JSONObject video = item.getJSONObject("video");
JSONObject playAddr = video.getJSONObject("play_addr");
JSONArray urlList = playAddr.getJSONArray("url_list");
List<String> urlListArr = JSONArray.parseObject(urlList.toJSONString(), new TypeReference<List<String>>(){});
if(urlListArr.size() == 0) {
return "";
}
return urlListArr.get(0);
// VideoConvert.record(finalAddr, "/home/yinyue/upload/红马.flv");
}

3、ffmpeg 拉流并保存
    /**
* 转存视频流
* @param input
* @param outFile
* @throws FrameGrabber.Exception
* @throws FrameRecorder.Exception
*/
public static void record(String input, String outFile) throws FrameGrabber.Exception, FrameRecorder.Exception {
FrameGrabber grabber = new FFmpegFrameGrabber(input);
grabber.start();
OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
Frame frame = grabber.grab();
opencv_core.IplImage image = null;
if(frame == null) {
System.out.println("第一帧为空,请检查视频源");
return;
}
image = converter.convert(frame);
FrameRecorder recorder = FrameRecorder.createDefault(outFile, frame.imageWidth, frame.imageHeight);
recorder.setVideoCodec(AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setFrameRate(25);
recorder.setGopSize(25);
recorder.start();
Frame saveFrame;
while((frame = grabber.grab()) != null) {
saveFrame = converter.convert(image);
// 获取类型, 视频或者音频
// EnumSet<Frame.Type> videoOrAudio = saveFrame.getTypes();
// 录制视频
recorder.record(saveFrame);
}
recorder.close();
}

七、运行截图

运行完成后本地成功生成了下载的视频文件

八、作者心得

我们生在一个技术百花齐放,日新月异的年代,生于这个时代即是幸运也是悲哀,在如此浩瀚无穷无尽的知识更迭浪潮中,很难保证全能,尽善尽美;有的人专注于算法,有的人专注与数据处理,还有的人动手能力不行,但是理论能力极强,比如著名物理学家杨振宁,有的人专注于如何应用落地,致力于将技术应用于社会生活,所以,如果本文对你有用,请不吝赞赏,如果你感觉内容过于浅薄或者是令你感到不适,也请缄默不言,互相留一份体面。

Javacv 音视频小工具 - 下载抖音视频的更多相关文章

  1. python+fiddler 抓取抖音数据包并下载抖音视频

    这个我们要下载视频,那么肯定首先去找抖音视频的url地址,那么这个地址肯定在json格式的数据包中,所以我们就去专门查看json格式数据包 这个怎么找我就不用了,直接看结果吧 你找json包,可以选大 ...

  2. Python音视频开发:消除抖音短视频Logo的图形化工具实现

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解>节介绍了怎么通过Python+Moviepy+OpenCV实现 ...

  3. Python音视频开发:消除抖音短视频Logo和去电视台标

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 对于带Logo(如抖音Logo.电视台标)的视频,有三种方案进行Logo消除: 直接将对应区域用对应图像替换: 直接将对应区域模糊化: 通过变换将要 ...

  4. (数据科学学习手札80)用Python编写小工具下载OSM路网数据

    本文对应脚本已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 我们平时在数据可视化或空间数据分析的过程中经常会 ...

  5. 用 Python 下载抖音无水印视频

    说起抖音,大家或多或少应该都接触过,如果大家在上面下载过视频,一定知道我们下载的视频是带有水印的,那么我们有什么方式下载不带水印的视频呢?其实用 Python 就可以做到,下面我们来看一下. 很多人学 ...

  6. Python MoviePy中文教程导览及可执行音视频剪辑工具下载

    ☞ ░ 前往老猿Python博文目录 ░ <Python音视频剪辑库MoviePy1.0.3中文教程导览及可执行工具下载>是老猿两个关于moviepy的专栏<PyQt+moviepy ...

  7. Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 对于带Logo(如抖音Logo.电视台标)的视频,有三种方案进行Logo消除: 直接将对应区域用对应图像替换: 直接将对应区域模糊化: 通过变换将要 ...

  8. Python爬虫---爬取抖音短视频

    目录 前言 抖音爬虫制作 选定网页 分析网页 提取id构造网址 拼接数据包链接 获取视频地址 下载视频 全部代码 实现结果 待解决的问题 前言 最近一直想要写一个抖音爬虫来批量下载抖音的短视频,但是经 ...

  9. 今日头条、抖音、西瓜、火山、微视、陌陌等自媒体平台小视频批量下载工具v1.1.0(视频搬运福利)

    前言 目前各大自媒体平台爆火,网络流量暴涨,各大自媒体平台的小视频为广大个广告主带来了如泉涌般的的视频流量,更给广大的自媒体小编带来了丰厚的利益回报,想要创做更多的自媒体内容着实不易,下面给广大的小视 ...

  10. 用Google Brain的机器学习项目:Magenta,教神经网络学抖音小姐姐作曲。

    先上我们要学习的小姐姐 的美照.. 一.配置环境 1.自己配置环境:python,tensorflow,bazel(编译),java.然后下载magenta(https://github.com/te ...

随机推荐

  1. 什么是spring框架

    一bai.概念:1. spring是开源的轻量级框架2 spring核心主要两部分:(1)aop:面向切面编程,扩展功能不是修改源代码实现(2)ioc:控制反转,- 比如有一个类,在类里面有方法(不是 ...

  2. 【Operating System】——An interesting question on Process Creation

    In the book Operating System Concepts- 9th Edition - Chapter 3 - Page 117 - Page 120 it says: How do ...

  3. Java基础Day5-数组

    一.数组声明创建 首先必须声明数组变量,才能在程序中使用数组. 声明数组变量的语法如下: dataType[] arrayRefVar; 例如: int[] nums; Java语言使用new操作符来 ...

  4. centos7(虚拟机)下安装nginx

    centos7(虚拟机)下安装nginx 安装前准备 gcc yum install gcc-c++ pcre yum install -y pcre pcre-devel zlib yum inst ...

  5. aos.js 与 swiper 组合,翻页后无法触发aos的效果

    手动给除第一页之外的需要特效的元素添加 class="aos-animate" 转自:https://cloud.tencent.com/developer/ask/sof/302 ...

  6. WebService基本功能接口实现

    一.web服务的创建 1.新建一个web服务 创建一个空的模板可以为后面添加服务 2.创建一个服务 3.创建好服务后可以在websevice里面添加一个接口方法 4.我们运行下我们写的简单接口方法是否 ...

  7. springmvc拦截器的简单创建

    找到前端控制器配置文件: 配置拦截器: 实现接口,定义自己的规则:

  8. IT工具知识-09:OpenWrt设置旁路由

    0.使用背景 设备:N1 固件:filppy打包的60+ 1.使用方法 1.1知道主路由的IP地址(例如192.168.1.1) 1.2在OpenWrt管理界面下进行设置 进入OpenWrt管理界面 ...

  9. 【JIRA】jira issue reindex

    参考文档: https://community.atlassian.com/t5/Marketplace-Apps-Integrations/Scriptrunner-Listener-Reindex ...

  10. RPC方式调用远程webservice接口

    /** * 可调整调用方法与命名空间的请求 * @param wsMethod 方法名 * @param bodyMessage json请求体.toString() * @return JSONOb ...