Push latest changes

This commit is contained in:
Daniel S. 2021-10-28 21:27:15 +02:00
parent be4930a8a3
commit aeb624bcf3
5 changed files with 578 additions and 320 deletions

View file

@ -2,37 +2,64 @@ import cffi
import os
import sys
import time
from dvdnav import DVDNav
from dvdnav import DVDNav,DVDError
from dvdread import DVDRead
import subprocess as SP
import json
from glob import glob
import itertools as ITT
from vob_demux import demux
from ff_d2v import make_d2v
def loadlib(dll_path, *includes, **kwargs):
ffi = cffi.FFI()
for include in includes:
ffi.cdef(open(include).read(), kwargs)
return ffi, ffi.dlopen(dll_path)
os.environ["DVDCSS_VERBOSE"]="2"
os.environ["DVDCSS_METHOD"]="disc"
for dvd_path in ITT.chain.from_iterable(map(glob,sys.argv[1:])):
r = DVDRead(dvd_path)
# r.grab_ifos()
# r.grab_vobs()
# exit()
r=DVDRead(sys.argv[1])
out_folder=os.path.join("out","_".join([r.disc_id,r.udf_disc_name or r.iso_disc_name]).replace(" ","_"))
del r
out_folder = os.path.join(
"out", "_".join([r.disc_id, r.udf_disc_name or r.iso_disc_name]).replace(" ", "_")
)
os.makedirs(out_folder, exist_ok=True)
d=DVDNav(sys.argv[1])
d = DVDNav(dvd_path)
to_demux = []
for k, v in d.titles.items():
v['duration']=v['duration'].total_seconds()
v['chapters']=[c.total_seconds() for c in v['chapters']]
v["duration"] = v["duration"].total_seconds()
v["chapters"] = [c.total_seconds() for c in v["chapters"]]
d.titles[k] = v
with open(os.path.join(out_folder,f"{k}.json"),"w") as fh:
with open(os.path.join(out_folder, f"{k:03}.json"), "w") as fh:
json.dump(v, fh)
for a in range(v['angles']):
a+=1
outfile=os.path.join(out_folder,f"{k}_{a}.vob")
with open(outfile,"wb") as fh:
for a in range(0,99):
block=0
outfile = os.path.join(out_folder, f"t{k:03}_a{a:03}_b{block:03}.vob")
to_demux.append(outfile)
fh = open(outfile, "wb")
try:
for block in d.get_blocks(k, a):
if isinstance(block, int):
outfile = os.path.join(out_folder, f"t{k:03}_a{a:03}_b{block:03}.vob")
to_demux.append(outfile)
if fh:
fh.close()
fh = open(outfile, "wb")
else:
fh.write(block)
demux(outfile)
os.unlink(outfile)
except DVDError as e:
if str(e)!="Invalid angle specified!":
raise
if fh.tell()==0:
fh.close()
os.unlink(fh.name)
while fh.name in to_demux:
to_demux.remove(fh.name)
for file in to_demux:
demux(file)
os.unlink(file)
for file in glob(os.path.join(out_folder,"*.m2v")):
make_d2v(file)

159
dvdnav.py
View file

@ -3,6 +3,8 @@ import os
import functools
from datetime import timedelta
from tqdm import tqdm
from dvdread import DVDRead
def loadlib(dll_path, *includes, **kwargs):
ffi = cffi.FFI()
@ -10,21 +12,29 @@ def loadlib(dll_path,*includes,**kwargs):
ffi.cdef(open(include).read(), kwargs)
return ffi, ffi.dlopen(dll_path)
class DVDError(Exception):
pass
class DVDNav(object):
def __init__(self,path,verbose=2,method="disc"):
def __init__(self, path, verbose=None, method="disc"):
if verbose is None:
os.environ.pop("DVDCSS_VERBOSE", None)
else:
os.environ["DVDCSS_VERBOSE"] = str(verbose)
os.environ["DVDCSS_METHOD"] = method
self.dvd = None
self.ffi,self.lib = loadlib("libdvdnav-4.dll",
self.ffi, self.lib = loadlib(
"libdvdnav-4.dll",
"dvd_types.h",
"dvd_reader.h",
"ifo_types.h",
"nav_types.h",
"dvdnav_events.h",
"dvdnav.h")
"dvdnav.h",
pack=True,
)
self.path = path
self.titles = {}
self.open(path)
@ -39,6 +49,15 @@ class DVDNav(object):
def get_blocks(self, title, angle=1, slang=None):
self.__check_error(self.lib.dvdnav_set_PGC_positioning_flag(self.dvd, 1))
self.__check_error(self.lib.dvdnav_title_play(self.dvd, title))
curr_angle = self.ffi.new("int32_t*", 0)
num_angles = self.ffi.new("int32_t*", 0)
self.__check_error(
self.lib.dvdnav_get_angle_info(self.dvd, curr_angle, num_angles)
)
if angle != 0:
if angle < 1 or angle > num_angles[0]:
raise DVDError("Invalid angle specified!")
if angle != curr_angle[0]:
self.__check_error(self.lib.dvdnav_angle_change(self.dvd, angle))
if slang is not None:
self.__check_error(self.lib.dvdnav_spu_language_select(self.dvd, slang))
@ -52,7 +71,7 @@ class DVDNav(object):
1: "FirstPlay",
2: "VTSTitle",
4: "VMGM",
8: "VTSMenu"
8: "VTSMenu",
}
events = {
0: "DVDNAV_BLOCK_OK",
@ -67,51 +86,83 @@ class DVDNav(object):
9: "DVDNAV_HIGHLIGHT",
10: "DVDNAV_SPU_CLUT_CHANGE",
12: "DVDNAV_HOP_CHANNEL",
13: "DVDNAV_WAIT"
13: "DVDNAV_WAIT",
}
progbar=tqdm(unit_divisor=1024,unit_scale=True,unit="iB",desc="Ripping DVD")
progbar = tqdm(
unit_divisor=1024,
unit_scale=True,
unit="iB",
desc="Ripping DVD",
disable=False,
)
ripped = set()
current_vts = None
current_cell = None
current_pg = None
while True:
self.__check_error(self.lib.dvdnav_get_next_block(self.dvd, buf, ev, size))
if self.lib.dvdnav_get_position(self.dvd,pos,total_size)==self.lib.DVDNAV_STATUS_OK:
if (
self.lib.dvdnav_get_position(self.dvd, pos, total_size)
== self.lib.DVDNAV_STATUS_OK
):
progbar.total = total_size[0] * 2048
progbar.n = max(progbar.n, min(progbar.total, pos[0] * 2048))
progbar.update(0)
progbar.set_postfix(
vts=current_vts,
cell=current_cell,
pg=current_pg,
angle=angle,
title=title,
)
# print("Got event:",events.get(ev[0],ev[0]),size[0])
if ev[0] in [self.lib.DVDNAV_SPU_CLUT_CHANGE,self.lib.DVDNAV_HOP_CHANNEL,self.lib.DVDNAV_NOP]:
if ev[0] in [
self.lib.DVDNAV_SPU_CLUT_CHANGE,
self.lib.DVDNAV_HOP_CHANNEL,
self.lib.DVDNAV_NOP,
self.lib.DVDNAV_HIGHLIGHT,
]:
continue
elif ev[0] == self.lib.DVDNAV_BLOCK_OK:
# print("Read",size[0])
yield self.ffi.buffer(buf, size[0])[:]
elif ev[0] == self.lib.DVDNAV_STOP:
progbar.write(f"[{title}|{angle}] Stop")
break
elif ev[0] == self.lib.DVDNAV_NAV_PACKET:
nav=self.lib.dvdnav_get_current_nav_pci(self.dvd)
# print("PTS:",timedelta(seconds=nav.pci_gi.vobu_s_ptm/90000))
pass
elif ev[0] == self.lib.DVDNAV_STILL_FRAME:
# print("Still")
self.__check_error(self.lib.dvdnav_still_skip(self.dvd))
elif ev[0] == self.lib.DVDNAV_WAIT:
# print("Wait",size[0])
self.__check_error(self.lib.dvdnav_wait_skip(self.dvd))
elif ev[0] == self.lib.DVDNAV_SPU_STREAM_CHANGE:
spu=self.ffi.cast("dvdnav_spu_stream_change_event_t*",buf)
progbar.write(f"[{title}|{angle}] SPU: Wide: {spu.physical_wide} Letterbox: {spu.physical_letterbox} Pan&Scan: {spu.physical_pan_scan} Logical: {spu.logical}")
pass
elif ev[0] == self.lib.DVDNAV_AUDIO_STREAM_CHANGE:
audio = self.ffi.cast("dvdnav_audio_stream_change_event_t*", buf)
progbar.write(f"[{title}|{angle}] Audio: Physical: {audio.physical} Logical: {audio.logical}")
elif ev[0] == self.lib.DVDNAV_CELL_CHANGE:
cell = self.ffi.cast("dvdnav_cell_change_event_t*", buf)
progbar.write(f"[{title}|{angle}] Cell: {cell.cellN} ({cell.cell_start}-{cell.cell_start+cell.cell_length}), PG: {cell.pgN} ({cell.pg_start}-{cell.pg_start+cell.pg_length})")
current_cell = cell.cellN
current_pg = cell.pgN
progbar.write(
f"[{title}|{angle}] Cell: {cell.cellN} ({cell.cell_start}-{cell.cell_start+cell.cell_length}), PG: {cell.pgN} ({cell.pg_start}-{cell.pg_start+cell.pg_length})"
)
elif ev[0] == self.lib.DVDNAV_VTS_CHANGE:
vts = self.ffi.cast("dvdnav_vts_change_event_t*", buf)
old_domain=sorted(domains[k] for k in domains if vts.old_domain&k)
new_domain=sorted(domains[k] for k in domains if vts.new_domain&k)
progbar.write(f"[{title}|{angle}] VTS: {vts.old_vtsN} ({vts.old_domain} {old_domain}) -> {vts.new_vtsN} ({vts.new_domain} {new_domain})")
if vts.new_domain==8: # back to menu
new_vts = (vts.new_vtsN, vts.new_domain)
ripped.add((vts.old_vtsN, vts.old_domain))
# progbar.write(f"[{title}|{angle}] VTS: {vts.old_vtsN} ({vts.old_domain} {old_domain}) -> {vts.new_vtsN} ({vts.new_domain} {new_domain})")
if new_vts in ripped: # looped
progbar.write(f"[{title}|{angle}] Looped!")
break
current_vts = (vts.new_vtsN, vts.new_domain)
if vts.new_domain == 8: # back to menu
progbar.write(f"[{title}|{angle}] Back to menu!")
break
yield vts.new_vtsN
else:
progbar.write(f"[{title}|{angle}] Unhandled: {events.get(ev[0],ev[0])} {size[0]}")
progbar.write(
f"[{title}|{angle}] Unhandled: {events.get(ev[0],ev[0])} {size[0]}"
)
self.__check_error(self.lib.dvdnav_stop(self.dvd))
def __check_error(self, ret):
if ret == self.lib.DVDNAV_STATUS_ERR:
@ -134,17 +185,19 @@ class DVDNav(object):
num_parts = titles[0]
self.lib.dvdnav_get_number_of_angles(self.dvd, title, titles)
num_angles = titles[0]
num_chapters=self.lib.dvdnav_describe_title_chapters(self.dvd,title,times,duration)
num_chapters = self.lib.dvdnav_describe_title_chapters(
self.dvd, title, times, duration
)
if duration[0] == 0:
continue
chapters = []
for t in range(num_chapters):
chapters.append(timedelta(seconds=times[0][t] / 90000))
self.titles[title] = {
'parts':num_parts,
'angles': num_angles,
'duration': timedelta(seconds=duration[0]/90000),
'chapters': chapters
"parts": num_parts,
"angles": num_angles,
"duration": timedelta(seconds=duration[0] / 90000),
"chapters": chapters,
}
def __get_info(self):
@ -165,48 +218,48 @@ class DVDNav(object):
self.__get_info()
for title in self.titles:
self.__check_error(self.lib.dvdnav_title_play(self.dvd, title))
self.titles[title]['audio']={}
self.titles[title]['subtitles']={}
self.titles[title]["audio"] = {}
self.titles[title]["subtitles"] = {}
for n in range(255):
stream_id = self.lib.dvdnav_get_audio_logical_stream(self.dvd, n)
if stream_id == -1:
continue
self.__check_error(self.lib.dvdnav_get_audio_attr(self.dvd,stream_id,audio_attrs))
self.__check_error(
self.lib.dvdnav_get_audio_attr(self.dvd, stream_id, audio_attrs)
)
alang = None
if audio_attrs.lang_type:
alang = str(audio_attrs.lang_code.to_bytes(2,'big'),"utf8")
alang = str(audio_attrs.lang_code.to_bytes(2, "big"), "utf8")
channels = audio_attrs.channels + 1
codec = {
0: 'ac3',
2: 'mpeg1',
3: 'mpeg-2ext',
4: 'lpcm',
6: 'dts'
}[audio_attrs.audio_format]
codec = {0: "ac3", 2: "mpeg1", 3: "mpeg-2ext", 4: "lpcm", 6: "dts"}[
audio_attrs.audio_format
]
audio_type = {
0: None,
1: 'normal',
2: 'descriptive',
1: "normal",
2: "descriptive",
3: "director's commentary",
4: "alternate director's commentary"
4: "alternate director's commentary",
}[audio_attrs.code_extension]
self.titles[title]['audio'][n]={
'stream_id': stream_id,
'lang':alang,
'channels': channels,
'codec': codec,
'type': audio_type
self.titles[title]["audio"][n] = {
"stream_id": stream_id,
"lang": alang,
"channels": channels,
"codec": codec,
"type": audio_type,
}
for n in range(255):
stream_id = self.lib.dvdnav_get_spu_logical_stream(self.dvd, n)
if stream_id == -1:
continue
self.__check_error(self.lib.dvdnav_get_spu_attr(self.dvd,stream_id,spu_attr))
self.__check_error(
self.lib.dvdnav_get_spu_attr(self.dvd, stream_id, spu_attr)
)
slang = None
if spu_attr.type == 1:
slang = str(spu_attr.lang_code.to_bytes(2,'big'),"utf8")
self.titles[title]['subtitles'][n]={
'stream_id': stream_id,
'lang':slang
slang = str(spu_attr.lang_code.to_bytes(2, "big"), "utf8")
self.titles[title]["subtitles"][n] = {
"stream_id": stream_id,
"lang": slang,
}
self.__check_error(self.lib.dvdnav_stop(self.dvd))

View file

@ -4,16 +4,24 @@ import functools
import binascii
from datetime import timedelta
def loadlib(dll_path, *includes, **kwargs):
ffi = cffi.FFI()
for include in includes:
ffi.cdef(open(include).read(), **kwargs)
return ffi, ffi.dlopen(dll_path)
class DVDRead(object):
def __init__(self,path):
def __init__(self, path, verbose="0", method="disc"):
if verbose is None:
os.environ.pop("DVDCSS_VERBOSE", None)
else:
os.environ["DVDCSS_VERBOSE"] = str(verbose)
os.environ["DVDCSS_METHOD"] = method
self.dvd = None
self.ffi,self.lib = loadlib("libdvdread-8.dll",
self.ffi, self.lib = loadlib(
"libdvdread-8.dll",
"dvd_types.h",
"dvd_reader.h",
"ifo_types.h",
@ -21,10 +29,124 @@ class DVDRead(object):
"ifo_print.h",
"nav_types.h",
"nav_read.h",
"nav_print.h")
"nav_print.h",
packed=True,
)
self.path = path
self.titles = {}
self.open(path)
self.lb_len=self.lib.DVD_VIDEO_LB_LEN
def grab_ifo(self,title,bup=False):
from tqdm import tqdm
buf = self.ffi.new("unsigned char[]", 512)
if bup:
fh = self.lib.DVDOpenFile(self.dvd, title, self.lib.DVD_READ_INFO_BACKUP_FILE)
else:
fh = self.lib.DVDOpenFile(self.dvd, title, self.lib.DVD_READ_INFO_FILE)
total_size = self.lib.DVDFileSize(fh)*self.lb_len
remaining = total_size
num_read = True
pbar = tqdm(total=total_size, unit="iB", unit_scale=True, unit_divisor=1024,leave=False)
while num_read:
num_read=self.lib.DVDReadBytes( fh, buf, 512)
num_read=min(num_read,remaining)
remaining-=num_read
pbar.update(num_read)
yield self.ffi.buffer(buf,num_read)[:]
self.lib.DVDCloseFile(fh)
def grab_ifos(self):
vmg_ifo = self.lib.ifoOpen(self.dvd, 0)
if vmg_ifo == self.ffi.NULL:
return
title_sets = vmg_ifo.vts_atrt.nr_of_vtss
for t in range(1,title_sets + 1):
vts = self.lib.ifoOpen(self.dvd, t)
if vts == self.ffi.NULL:
continue
self.lib.ifoClose(vts)
outfile=os.path.join("RIP",f"VTS_{t:02}_0.ifo")
with open(outfile, "wb") as out_ifo:
for block in self.grab_ifo(t,bup=False):
out_ifo.write(block)
outfile=os.path.join("RIP",f"VTS_{t:02}_0.bup")
with open(outfile, "wb") as out_ifo:
for block in self.grab_ifo(t,bup=True):
out_ifo.write(block)
self.lib.ifoClose(vmg_ifo)
def grab_vob(self,title):
from tqdm import tqdm
buf = self.ffi.new("unsigned char[]", 512 * self.lb_len)
fh = self.lib.DVDOpenFile(self.dvd, title, self.lib.DVD_READ_TITLE_VOBS)
total_size = self.lib.DVDFileSize(fh)*self.lb_len
remaining = total_size
num_read = True
pos=0
pbar = tqdm(total=total_size, unit="iB", unit_scale=True, unit_divisor=1024,leave=False)
while remaining:
num_read=self.lib.DVDReadBlocks( fh,pos, 512, buf)
if num_read<0:
raise RuntimeError("Error reading!")
num_read_bytes=num_read*self.lb_len
num_read_bytes=min(num_read_bytes,remaining)
remaining-=num_read_bytes
pbar.update(num_read_bytes)
yield self.ffi.buffer(buf,num_read_bytes)[:]
pos+=num_read
pbar.close()
self.lib.DVDCloseFile(fh)
def grab_vobs(self):
vmg_ifo = self.lib.ifoOpen(self.dvd, 0)
if vmg_ifo == self.ffi.NULL:
return
title_sets = vmg_ifo.vts_atrt.nr_of_vtss
for t in range(1,title_sets + 1):
vts = self.lib.ifoOpen(self.dvd, t)
if vts == self.ffi.NULL:
continue
self.lib.ifoClose(vts)
outfile=os.path.join("RIP",f"VTS_{t:02}_0.vob")
with open(outfile, "wb") as out_ifo:
for block in self.grab_vob(t):
out_ifo.write(block)
self.lib.ifoClose(vmg_ifo)
def test(self):
from tqdm import tqdm
fn = 0
chunk_size = 2048
buf = self.ffi.new("unsigned char[]", chunk_size * 2048)
for fn in range(title_sets + 1):
pos = 0
fh = self.lib.DVDOpenFile(self.dvd, fn, self.lib.DVD_READ_TITLE_VOBS)
if fh:
total_size = self.lib.DVDFileSize(fh)
if total_size == -1:
self.lib.DVDCloseFile(fh)
break
pbar = tqdm(total=total_size * 2048, unit="iB", unit_scale=True, unit_divisor=1024,leave=False)
last=False
with open(f"out_{fn}.vob", "wb") as out_vob:
while True:
if (pos+chunk_size)>total_size:
chunk_size=total_size-pos
count = self.lib.DVDReadBlocks(fh, pos, chunk_size, buf)
if count == -1:
break
pbar.update(
out_vob.write(self.ffi.buffer(buf, count * 2048)[:])
)
pos += count
if pos>=total_size:
break
self.lib.DVDCloseFile(fh)
fn += 1
if fn>200:
break
def __del__(self):
if self.dvd:

112
ff_d2v.py
View file

@ -23,15 +23,12 @@ colorspace={
"ictcp": 14,
}
pict_types={
'I':0b01,
'P':0b10,
'B':0b11
}
pict_types = {"I": 0b01, "P": 0b10, "B": 0b11}
def make_info(frames):
has_interlaced = any(frame['interlaced_frame'] for frame in frames)
new_gop='timecode' in frames[0].get('tags',{})
has_interlaced = any(frame["interlaced_frame"] for frame in frames)
new_gop = "timecode" in frames[0].get("tags", {})
info = 0x000
info |= 1 << 11 # always 1
info |= 0 << 10 # 0=Closed GOP, 1=Open GOP
@ -39,15 +36,16 @@ def make_info(frames):
info |= new_gop << 8
return info
def make_flags(frames):
flags = []
for frame in frames:
needs_prev = False
progressive=not int(frame['interlaced_frame'])
pt=pict_types[frame['pict_type']]
progressive = not int(frame["interlaced_frame"])
pt = pict_types[frame["pict_type"]]
reserved = 0b00
tff=int(frame['top_field_first'])
rff=int(frame['repeat_pict'])
tff = int(frame["top_field_first"])
rff = int(frame["repeat_pict"])
flag = 0b0
flag |= (not needs_prev) << 7
flag |= progressive << 6
@ -61,9 +59,9 @@ def make_flags(frames):
def make_line(frames, stream):
info = f"{make_info(frames):03x}"
matrix=colorspace[stream['color_space']]
matrix = colorspace[stream["color_space"]]
file = 0
position=frames[0]['pkt_pos']
position = frames[0]["pkt_pos"]
skip = 0
vob = 0
cell = 0
@ -72,16 +70,27 @@ def make_line(frames,stream):
def get_frames(path):
proc=SP.Popen([
proc = SP.Popen(
[
"ffprobe",
"-probesize", str(0x7fffffff),
"-analyzeduration", str(0x7fffffff),
"-v","fatal",
"-i",path,
"-select_streams","v:0",
"-probesize",
str(0x7FFFFFFF),
"-analyzeduration",
str(0x7FFFFFFF),
"-v",
"fatal",
"-i",
path,
"-select_streams",
"v:0",
"-show_frames",
"-print_format","compact"
],stdout=SP.PIPE,stdin=SP.DEVNULL,bufsize=0)
"-print_format",
"compact",
],
stdout=SP.PIPE,
stdin=SP.DEVNULL,
bufsize=0,
)
data = None
for line in proc.stdout:
line = str(line, "utf8").strip().split("|")
@ -92,30 +101,44 @@ def get_frames(path):
exit(ret)
return data
def get_streams(path):
proc=SP.Popen([
proc = SP.Popen(
[
"ffprobe",
"-probesize", str(0x7fffffff),
"-analyzeduration", str(0x7fffffff),
"-v","fatal",
"-i",path,
"-select_streams","v:0",
"-probesize",
str(0x7FFFFFFF),
"-analyzeduration",
str(0x7FFFFFFF),
"-v",
"fatal",
"-i",
path,
"-select_streams",
"v:0",
"-show_streams",
"-show_format",
"-print_format","json"
],stdout=SP.PIPE,stdin=SP.DEVNULL,bufsize=0)
"-print_format",
"json",
],
stdout=SP.PIPE,
stdin=SP.DEVNULL,
bufsize=0,
)
data = json.load(proc.stdout)
ret = proc.wait()
if ret != 0:
exit(ret)
return data['streams'],data['format']
return data["streams"], data["format"]
def make_header(file):
return ["DGIndexProjectFile16", "1", os.path.abspath(file)]
def make_settings(stream):
pict_size = "x".join(map(str, [stream["width"], stream["height"]]))
frame_rate = list(map(int,stream['r_frame_rate'].split("/")))
frame_rate = list(map(int, stream["r_frame_rate"].split("/")))
frame_rate = (frame_rate[0] * 1000) // frame_rate[1]
frame_rate = f"{frame_rate} ({stream['r_frame_rate']})"
header = [
@ -129,37 +152,46 @@ def make_settings(stream):
("Picture_Size", pict_size),
("Field_Operation", 0), # Honor Pulldown Flags
("Frame_Rate", frame_rate),
("Location",f"0,0,0,0"),
("Location", "0,0,0,0"),
]
for k, v in header:
yield f"{k}={v}"
def gen_d2v(path):
yield from make_header(path)
yield ""
streams, fmt = get_streams(path)
stream=[s for s in streams if s['codec_type']=='video'][0]
stream['index']=str(stream['index'])
stream = [s for s in streams if s["codec_type"] == "video"][0]
stream["index"] = str(stream["index"])
yield from make_settings(stream)
yield ""
line_buffer = []
frames = get_frames(path)
prog_bar=tqdm(frames,total=int(fmt['size']),unit_divisor=1024,unit_scale=True,unit="iB",dec="Writing d2v")
prog_bar = tqdm(
frames,
total=int(fmt["size"]),
unit_divisor=1024,
unit_scale=True,
unit="iB",
desc="Writing d2v",
)
for line in prog_bar:
if 'frame' not in line:
if "frame" not in line:
continue
frame=line['frame']
prog_bar.n=min(max(prog_bar.n,int(frame['pkt_pos'])),int(fmt['size']))
frame = line["frame"]
prog_bar.n = min(max(prog_bar.n, int(frame["pkt_pos"])), int(fmt["size"]))
prog_bar.update(0)
if frame['stream_index']!=stream['index']:
if frame["stream_index"] != stream["index"]:
continue
if frame['pict_type']=="I" and line_buffer:
if frame["pict_type"] == "I" and line_buffer:
yield make_line(line_buffer, stream)
line_buffer.clear()
line_buffer.append(frame)
prog_bar.close()
yield None
def make_d2v(path):
outfile = os.path.splitext(os.path.basename(path))[0]
outfile = os.path.extsep.join([outfile, "d2v"])

View file

@ -5,28 +5,40 @@ import subprocess as SP
def get_streams(path):
proc=SP.Popen([
proc = SP.Popen(
[
"ffprobe",
"-probesize", str(0x7fffffff),
"-analyzeduration", str(0x7fffffff),
"-v","fatal",
"-i",path,
"-probesize",
str(0x7FFFFFFF),
"-analyzeduration",
str(0x7FFFFFFF),
"-v",
"fatal",
"-i",
path,
"-show_streams",
"-show_format",
"-print_format","json"
],stdout=SP.PIPE,stdin=SP.DEVNULL,bufsize=0)
"-print_format",
"json",
],
stdout=SP.PIPE,
stdin=SP.DEVNULL,
bufsize=0,
)
data = json.load(proc.stdout)
ret = proc.wait()
if ret != 0:
return [], {}
return data['streams'],data['format']
return data["streams"], data["format"]
types = {
'mpeg2video': 'm2v',
'ac3': 'ac3',
'dvd_subtitle': 'sup',
"mpeg2video": "m2v",
"ac3": "ac3",
"dvd_subtitle": "sup",
}
def demux(path):
folder = os.path.dirname(path)
basename = os.path.splitext(os.path.basename(path))[0]
@ -34,30 +46,42 @@ def demux(path):
cmd = [
"ffmpeg",
"-y",
"-strict","-2",
"-fflags","+genpts",
"-probesize", str(0x7fffffff),
"-analyzeduration", str(0x7fffffff),
"-i",path,
"-scodec","copy",
"-vcodec","copy",
"-acodec","copy",
# "-fflags","+genpts+igndts",
"-probesize",
str(0x7FFFFFFF),
"-analyzeduration",
str(0x7FFFFFFF),
"-i",
path,
"-strict",
"-2",
"-vcodec",
"copy",
"-acodec",
"copy",
"-scodec",
"copy",
]
need_ffmpeg = False
for stream in streams:
codec=stream['codec_name']
codec = stream["codec_name"]
ext = types.get(codec, codec)
idx=stream['index']
hex_id=stream['id']
codec_name=stream['codec_long_name']
idx = stream["index"]
hex_id = stream["id"]
codec_name = stream["codec_long_name"]
outfile = os.path.join(folder, f"{basename}_{idx}_{hex_id}")
if codec=="dvd_nav_packet":
continue
print(idx, hex_id, codec_name, codec)
if codec == "dvd_subtitle":
SP.check_call([
"mencoder",path,"-vobsuboutindex",str(idx),"-vobsubout", outfile,"-nosound","-ovc", "copy", "-o",os.devnull
])
continue
print(idx,hex_id,codec_name,codec)
cmd+=["-map",f"0:#{hex_id}",outfile+f".{ext}"]
cmd += ["-map", f"0:#{hex_id}", "-strict", "-2", outfile + f".{ext}"]
need_ffmpeg = True
if need_ffmpeg:
SP.check_call(cmd)
if __name__=="__main__":
demux(sys.argv[1])