0. 개발 환경

  • OS : Windows 10 64bit
  • VS : Visual Studio 2010 (최신 버전도 상관없음)

 

1. 라이브러리 설치

가장 먼저, FFmpeg 홈페이지에 들어가 라이브러리를 다운받아야 합니다.

 

ffmpeg.org/download.html

 

Download FFmpeg

If you find FFmpeg useful, you are welcome to contribute by donating. More downloading options Git Repositories Since FFmpeg is developed with Git, multiple repositories from developers and groups of developers are available. Releases Approximately every 6

ffmpeg.org

여기서 헷갈렸던게 빌드되어 있는 라이브러리 파일이 보이지 않아 cygwin으로 컴파일 시도하고, 에러 원인 찾고 아주 난리난리였네요... 😂😂😂

 

특히 많은 블로그 글에서 zeranoe라는 사이트를 통해 라이브러리를 다운받던데, 해당 사이트는 서비스 중단되어 있고 다른 사이트가 있어서 더 헷갈렸습니다.

 

어찌어찌 잘 찾아보니 빌드된 파일이 있길래 ㅎㅎ; 정리해서 올립니다.

 

링크를 타고 들어가면 위와 같은 화면이 뜹니다.

 

여기서 빨간색 테두리를 친 링크를 누릅니다.

 

그러면 FFmpeg의 자동 빌드 파일을 다운받을 수 있는 깃허브 페이지로 이동하게 됩니다.

 

빌드 항목 중 빨간색 테두리를 친 shared 파일 중 하나를 선택하면 되는데, 둘의 차이는 gpl/lgpl 라이선스 차이이니 본인이 잘 판단하시면 됩니다.

 

gpl은 프로그램 소스코드의 공개 의무를 가지게 되고, lgpl은 동적 라이브러리를 참조하는 경우 프로그램 소스코드는 공개하지 않고 FFmpeg의 소스코드만 공개하면 됩니다.

 

저는 일단 gpl로 했었으나 lgpl로 변경할 예정입니다.

 

원하는 위치에 압축 파일을 해제하면 위와 같은 디렉토리 구조가 나타납니다.

 

lib에는 .lib 파일이, include에는 .h 파일이, bin에는 .dll 파일이 들어있습니다.

 

디렉토리 이름은 VS에서 설정하기 쉽게 간단한 이름으로 수정하시는게 좋습니다.

 

2. VS 프로젝트 설정

FFmpeg을 사용할 새 프로젝트를 생성합니다.

 

사진에는 이름에 'test'라고 써져 있는데 실제로는 'Decoder'로 생성했으니 참고해주길 바랍니다.

 

빌드를 빠르게 하기 위해 미리 컴파일된 헤더도 추가해줍니다.

 

생성이 완료되면 위와 같은 프로젝트 구조를 확인할 수 있을 겁니다.

 

이제 라이브러리 경로를 설정해주기 위해 프로젝트를 우클릭하고 속성 페이지를 엽니다.

 

구성 속성 > VC++ 디렉터리 항목을 클릭합니다.

 

여기서

 

  • 포함 디렉터리 : "FFmpeg 설치 경로\include"
  • 라이브러리 디렉터리 : "FFmpeg 설치 경로\lib"

와 같이 경로를 입력해줍니다.

 

그리고 프로젝트를 빌드하고 바로 실행할 수 있도록 구성 속성 > 디버깅 항목 중 환경 속성 값에 "FFmpeg 설치 경로\bin"을 설정해줍니다.

 

(2020-09-29 추가) ~

 

위와 같이 설정을 해도 '확인할 수 없는 외부 참조입니다.' 에러가 뜨는 경우

 

이는 FFMPEG과 플랫폼이 맞지 않아 발생하는 문제입니다.

 

Visual Studio 디버깅 버튼 옆의 플랫폼 메뉴에서 '구성 관리자...'을 클릭합니다.

 

저는 먼저 작업을 진행해서 'x64' 항목이 있지만, 보통은 'Win32'만 존재합니다.

 

활성 솔루션 플랫폼을 누르면 보이는 '<새로 만들기...>' 항목을 클릭합니다.

 

그러면 Visual Studio가 알아서 x64 플랫폼 정보를 입력해줍니다.

 

확인을 클릭해 x64 플랫폼을 만들 해당 플랫폼을 활성화합니다.

 

이제 빌드가 쨘! 😋

~ (2020-09-29 추가)

3. 컴파일

FFmpeg 로그를 출력하는 간단한 예제를 통해 설치가 정상적으로 이뤄졌는지 확인해보겠습니다.

 

stdafx.h

 

Decoder.cpp

 

헤더 파일과 메인 cpp 파일에 위와 같이 코드를 작성한 뒤 프로그램을 실행하면 아래와 같이 출력이 되어야 합니다.

 

 

4. VS 2010 추가 설정

 

만약 VS 2010을 사용하신다면, common.h에서 inttypes.h를 찾을 수 없다는 오류가 뜰 겁니다. 🤨

 

사실 FFmpeg이 지원하는(빌드 가능한) VS 버전이 2013부터인데, VS 2010에는 inttypes.h이 포함되어 있지 않아 발생하는 문제입니다.

 

이를 해결하기 위해 inttypes.h 파일을 따로 다운로드 받습니다.

 

code.google.com/archive/p/msinttypes/

 

Google Code Archive - Long-term storage for Google Code Project Hosting.

 

code.google.com

다운로드 받은 압축 파일을 보면 헤더 파일이 두 가지가 있을텐데, 하나는 이미 VS 2010에 포함되어 있으므로 inttypes.h만 "ffmpeg 설치 경로/include/libavutil"에 복사해 넣어줍니다.

 

그리고 같은 경로의 common.h 헤더 파일을 열고 아래와 같이 코드를 수정합니다.

 

#include <inttypes.h>

->

#include "inttypes.h"

 

이 상태로 프로젝트를 실행하면 정상적으로 수행되는 것을 확인할 수 있습니다.


WRITTEN BY
pf1999
프로그래밍 도중 삽질하다가 해결한 내용 공유용 블로그

,

 

검색을 해보면 알겠지만 DataTables를 사용할 때 Spring Boot의 Controller를 어떻게 짜야하는지 나와있는 곳이 없다.

 

그나마 있는 것들도 내 프로젝트에 제대로 적용이 안 됐다.

 

하루종일 스트레스를 받으며 삽질을 하다가 겨우 성공해서 해당 코드를 올린다.

 

1. View 단

 

 

hbody는 필요하지 않다.

 

 

2. JS 단

 

DataTables는 기본적으로 모든 데이터를 한 번에 통째로 들고 와 처리한다.

 

이러면 페이지 로딩이 버벅거릴 수 있으므로 serverSideprocessing 파라미터를 true로 설정한다.

 

위 두 파라미터를 설정하면 서버에 추가 작업이 필요하지만, 필요한 부분의 데이터만 가져올 수 있어 성능 향상에 도움이 된다.

 

destory 파라미터는 해당 테이블을 제거하고 다시 생성하기 위해 추가했다.

계속 reinitialize를 할 수 없다는 에러가 나와서 그냥 기존 테이블은 버리도록 했다.

 

ajax 파라미터는 로딩할 데이터를 서버에 비동기로 요청할 때 사용한다.

JQuery의 ajax와 매우 유사하다. type'post'로, url은 컨트롤러 맵핑에 맞게 입력해준다.

다른 예제를 찾다보면 datadataSrc라는 파라미터를 사용하는데, data는 서버에 전송할 데이터(검색에 필요한 조건 등)를, dataSrc는 서버에서 반환한 데이터를 후처리할 때 사용한다.

 

둘 다 필요 없으므로 설정하지 않았다.

 

 

3. Spring Boot Controller 단

 

가장 시간이 오래 걸린 부분이 맵핑 메소드에서 도대체 무슨 데이터를 반환해야 하는지였다.

 

모든 예제가 메소드 인자도 다르고, 반환 값도 다르다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ... 

 

ajax 404 에러, 서버 내부 에러 등 여러 장애를 지나 결국 방법을 찾았다.

 

먼저, @PostMapping으로 라우팅을 잡아준다.

 

그리고 반환 값은 기존 jQuery의 ajax post 요청을 처리할 때와 똑같이 해줬다.

 

메소드 인자는 req, res 순으로 정했는데, 다른 예제를 보면 (HttpServlet* 클래스는 아니지만) 두 개의 순서가 바뀌어 있는 경우도 많았다.

내부에서 알아서 처리하는 건지 잘 모르겠다.

이 부분은 검색이 필요하다.

 

req에는 datatables가 보낸 draw, start, length 파라미터가 들어있다.

 

drawCross Site Scripting 공격을 방지하기 위한 파라미터로, req에 담긴 값을 int로 변환해 그대로 반환해주면 된다.

 

-----------------------------
-> response, draw = 1 : OK
-> response, draw = 2 : OK
-> response, draw = 1 : ERROR
-----------------------------

 

위와 같이 draw가 순차적으로 진행되지 않고 다른 값이 들어올 경우(이 경우가 Cross Site Scripting 공격)를 처리한다.

draw의 값은 datatables 내부에서 자동으로 처리하므로 클라이언트나 서버에서 값을 건드리지 않아도 된다.

 

startlength는 테이블 페이징에 필요한 파라미터다.

start번 째 데이터부터 length개의 데이터를 요청한다는 의미다.

이 예제에서는 값을 상수로 반환하지만, 나중에 실제 DBMS에 접근할 경우 해당 파라미터를 이용해 필요한 값만 뽑아 반환하면 DataTables가 알아서 표시해준다.

 

반환 데이터를 json string 타입으로 반환할 예정이므로

 

res.setContentType("application/json");

이렇게 타입을 설정해준다.

 

이후에는 반환할 전체 json string을 구축하는 과정이다.

 

마지막으로 해당 값을 반환하면 끝이다.

 

return ResponseEntity.ok(sb.toString());

 


WRITTEN BY
pf1999
프로그래밍 도중 삽질하다가 해결한 내용 공유용 블로그

,

 When I tried to run hadoop hdfs writing program, it returns IOException like follow.

java.io.IOException: No FileSystem for scheme: hdfs

        at org.apache.hadoop.fs.FileSystem.getFileSystemClass(FileSystem.java:2586)

        at org.apache.hadoop.fs.FileSystem.createFileSystem(FileSystem.java:2593)

        at org.apache.hadoop.fs.FileSystem.access$200(FileSystem.java:91)

        at org.apache.hadoop.fs.FileSystem$Cache.getInternal(FileSystem.java:2632)

        at org.apache.hadoop.fs.FileSystem$Cache.get(FileSystem.java:2614)

        at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:370)

        at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:169)

        at co.kr.pf1999.common.HDFSHandler.<init>(HDFSHandler.java:21)

        at co.kr.pf1999.common.HDFSHandler$Singleton.<clinit>(HDFSHandler.java:14)

        at co.kr.pf1999.common.HDFSHandler.getInstance(HDFSHandler.java:54)

        at co.kr.pf1999.consumer.streamer.Streamer.lambda$0(Streamer.java:34)

        at java.lang.Iterable.forEach(Iterable.java:75)

        at co.kr.pf1999.consumer.streamer.Streamer.run(Streamer.java:32)


 Below 2 lines can solve this error.




WRITTEN BY
pf1999
프로그래밍 도중 삽질하다가 해결한 내용 공유용 블로그

,

Load it using its own url.


'compileOnly' command is working like maven's 'provided'. If specified, it will not compile libraries when release. Necessary for version independent.


WRITTEN BY
pf1999
프로그래밍 도중 삽질하다가 해결한 내용 공유용 블로그

,

I met below exception while developing kafka consumer.


java.sql.SQLException: No suitable driver found for jdbc:<db_name>://<server>:<port>/<db>

        at java.sql.DriverManager.getConnection(DriverManager.java:689)

        at java.sql.DriverManager.getConnection(DriverManager.java:247)

        at co.kr.realtimetech.sql.KairosDBHandler.<init>(DBHandler.java:20)

        at co.kr.realtimetech.consumer.streamer.StreamerFactory.start(StreamerFactory.java:44)

        at co.kr.realtimetech.Main.main(Main.java:18)


Actually, it is really simple and basic error...

Add Class.forName to load the driver class like below. 




WRITTEN BY
pf1999
프로그래밍 도중 삽질하다가 해결한 내용 공유용 블로그

,

 If execute above code via spark cluster, it will occur java.lang.NullPointerException like below.


ResultStage 1 (foreach at Streamer.java:52) failed in 0.257 s due to Job aborted due to stage failure: Task 1 in stage 1.0 failed 4 times, most recent failure: Lost task 1.3 in stage 1.0 (TID 10, 192.168.200.77, executor 4): java.lang.NullPointerException

        at co.kr.rollupcat.consumer.streamer.Streamer.lambda$null$d469caef$1(Streamer.java:56)

        at org.apache.spark.api.java.JavaRDDLike$$anonfun$foreach$1.apply(JavaRDDLike.scala:351)

        at org.apache.spark.api.java.JavaRDDLike$$anonfun$foreach$1.apply(JavaRDDLike.scala:351)

        at scala.collection.Iterator$class.foreach(Iterator.scala:893)

        at scala.collection.AbstractIterator.foreach(Iterator.scala:1336)

        at org.apache.spark.rdd.RDD$$anonfun$foreach$1$$anonfun$apply$28.apply(RDD.scala:921)

        at org.apache.spark.rdd.RDD$$anonfun$foreach$1$$anonfun$apply$28.apply(RDD.scala:921)

        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:2074)

        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:2074)

        at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)

        at org.apache.spark.scheduler.Task.run(Task.scala:109)

        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:345)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

        at java.lang.Thread.run(Thread.java:748)


 At first, I thought the reason is because the Connection object that used inside of hub.insert(record); is shared between other spark slave nodes, so I changed it to Connection pool but error is not fixed.


 And then, I searched google and found this.


https://stackoverflow.com/questions/26582617/spark-nullpointerexception-inside-foreach-loop


 Following answer of this, we should not use RDD Operations inside of RDD Operations. Well, so I followed like below.


 It's working fine, hurray!


WRITTEN BY
pf1999
프로그래밍 도중 삽질하다가 해결한 내용 공유용 블로그

,