init
This commit is contained in:
@@ -1,2 +1,5 @@
|
|||||||
# bluray
|
libbluray的go封装 只支持了部分接口
|
||||||
|
私人使用的库 只测试了其中的部分接口
|
||||||
|
|
||||||
|
The Go wrapper for libbluray only supports a subset of the interfaces
|
||||||
|
This is a private library, and only some of the interfaces have been tested
|
||||||
62
bluray_test.go
Normal file
62
bluray_test.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package bluray
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetBDVersion(t *testing.T) {
|
||||||
|
version := GetBDVersion()
|
||||||
|
if version == "" {
|
||||||
|
t.Errorf("Expected non-empty version string, got empty string")
|
||||||
|
}
|
||||||
|
fmt.Printf("libbluray version: %s\n", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBD(t *testing.T) {
|
||||||
|
db := BDOpen("path")
|
||||||
|
if db == nil {
|
||||||
|
t.Errorf("Expected non-nil DBStruct, got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := db.GetDiscInfo()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected non-nil BlurayDiscInfo, got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := json.Marshal(info)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("json.Marshal failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(string(res))
|
||||||
|
|
||||||
|
num, err := db.GetTitles(TitlesAll, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected non-nil BlurayTitle, got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("titles: %v\n", num)
|
||||||
|
|
||||||
|
mainTitle, err := db.GetMainTitle()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected non-nil BlurayTitle, got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("main title: %v\n", mainTitle)
|
||||||
|
|
||||||
|
tInfo, err := db.GetTitleInfo(mainTitle, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected non-nil BlurayTitle, got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = json.Marshal(tInfo)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("json.Marshal failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(string(res))
|
||||||
|
}
|
||||||
487
libbluray.go
Normal file
487
libbluray.go
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
package bluray
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: libbluray
|
||||||
|
#include <libbluray/bluray.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetBDVersion() string {
|
||||||
|
var major, minor, micro C.int
|
||||||
|
C.bd_get_version(&major, &minor, µ)
|
||||||
|
return fmt.Sprintf("%d.%d.%d", int(major), int(minor), int(micro))
|
||||||
|
}
|
||||||
|
|
||||||
|
type DBStruct struct {
|
||||||
|
bd *C.BLURAY
|
||||||
|
}
|
||||||
|
|
||||||
|
// BDOpen 打开蓝光光盘
|
||||||
|
func BDOpen(path string) *DBStruct {
|
||||||
|
cPath := C.CString(path)
|
||||||
|
defer C.free(unsafe.Pointer(cPath))
|
||||||
|
|
||||||
|
return &DBStruct{bd: C.bd_open(cPath, nil)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDiscInfo 获取蓝光光盘信息
|
||||||
|
// const BlurayDiscInfo *bd_get_disc_info(BLURAY *bd);
|
||||||
|
func (d *DBStruct) GetDiscInfo() (*BlurayDiscInfo, error) {
|
||||||
|
cInfo := C.bd_get_disc_info(d.bd) // 调用 C 函数获取 BluRay 光盘信息
|
||||||
|
|
||||||
|
convertCToGoTitle := func(cTitle *C.BLURAY_TITLE) *BlurayTitle {
|
||||||
|
if cTitle == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &BlurayTitle{
|
||||||
|
Name: cStringToGoString(cTitle.name),
|
||||||
|
Interactive: uint8(cTitle.interactive),
|
||||||
|
Accessible: uint8(cTitle.accessible),
|
||||||
|
Hidden: uint8(cTitle.hidden),
|
||||||
|
BDJ: uint8(cTitle.bdj),
|
||||||
|
IDRef: uint32(cTitle.id_ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cInfo == nil {
|
||||||
|
return nil, fmt.Errorf("failed to get disc info")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换 Titles 数组
|
||||||
|
var goTitles []*BlurayTitle
|
||||||
|
if cInfo.num_titles > 0 && cInfo.titles != nil {
|
||||||
|
// 遍历 C 数组,转换每个 BLURAY_TITLE
|
||||||
|
cTitles := (*[1 << 30]*C.BLURAY_TITLE)(unsafe.Pointer(cInfo.titles))[:cInfo.num_titles:cInfo.num_titles]
|
||||||
|
for _, cTitle := range cTitles {
|
||||||
|
goTitles = append(goTitles, convertCToGoTitle(cTitle))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goInfo := &BlurayDiscInfo{
|
||||||
|
BlurayDetected: uint8(cInfo.bluray_detected),
|
||||||
|
DiscName: cStringToGoString(cInfo.disc_name),
|
||||||
|
UDFVolumeID: cStringToGoString(cInfo.udf_volume_id),
|
||||||
|
DiscID: *(*[20]byte)(unsafe.Pointer(&cInfo.disc_id)),
|
||||||
|
NoMenuSupport: uint8(cInfo.no_menu_support),
|
||||||
|
FirstPlaySupported: uint8(cInfo.first_play_supported),
|
||||||
|
TopMenuSupported: uint8(cInfo.top_menu_supported),
|
||||||
|
NumTitles: uint32(cInfo.num_titles),
|
||||||
|
Titles: goTitles,
|
||||||
|
FirstPlay: convertCToGoTitle(cInfo.first_play),
|
||||||
|
TopMenu: convertCToGoTitle(cInfo.top_menu),
|
||||||
|
NumHDMVTitles: uint32(cInfo.num_hdmv_titles),
|
||||||
|
NumBDJTitles: uint32(cInfo.num_bdj_titles),
|
||||||
|
NumUnsupportedTitles: uint32(cInfo.num_unsupported_titles),
|
||||||
|
BDJDetected: uint8(cInfo.bdj_detected),
|
||||||
|
BDJSupported: uint8(cInfo.bdj_supported),
|
||||||
|
LibJVMDetected: uint8(cInfo.libjvm_detected),
|
||||||
|
BDJHandled: uint8(cInfo.bdj_handled),
|
||||||
|
BDJOrgID: *(*[9]byte)(unsafe.Pointer(&cInfo.bdj_org_id)),
|
||||||
|
BDJDiscID: *(*[33]byte)(unsafe.Pointer(&cInfo.bdj_disc_id)),
|
||||||
|
VideoFormat: uint8(cInfo.video_format),
|
||||||
|
FrameRate: uint8(cInfo.frame_rate),
|
||||||
|
ContentExist3D: uint8(cInfo.content_exist_3D),
|
||||||
|
InitialOutputModePreference: uint8(cInfo.initial_output_mode_preference),
|
||||||
|
ProviderData: *(*[32]byte)(unsafe.Pointer(&cInfo.provider_data)),
|
||||||
|
AACDetected: uint8(cInfo.aacs_detected),
|
||||||
|
LibAACSDetected: uint8(cInfo.libaacs_detected),
|
||||||
|
AACSHandled: uint8(cInfo.aacs_handled),
|
||||||
|
AACSErrorCode: int(cInfo.aacs_error_code),
|
||||||
|
AACSMKBV: int(cInfo.aacs_mkbv),
|
||||||
|
BDPlusDetected: uint8(cInfo.bdplus_detected),
|
||||||
|
LibBDPlusDetected: uint8(cInfo.libbdplus_detected),
|
||||||
|
BDPlusHandled: uint8(cInfo.bdplus_handled),
|
||||||
|
BDPlusGen: uint8(cInfo.bdplus_gen),
|
||||||
|
BDPlusDate: uint32(cInfo.bdplus_date),
|
||||||
|
InitialDynamicRangeType: uint8(cInfo.initial_dynamic_range_type),
|
||||||
|
}
|
||||||
|
|
||||||
|
return goInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMetaFile 获取蓝光光盘的元数据文件
|
||||||
|
|
||||||
|
const (
|
||||||
|
TitlesAll = 0
|
||||||
|
TitlesFilterDupTitle = 0x01
|
||||||
|
TitlesFilterDupClip = 0x02
|
||||||
|
TitlesRelevant = TitlesFilterDupTitle | TitlesFilterDupClip
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetTitles 获取标题数量(播放列表)
|
||||||
|
// GetTitles Get the number of titles (playlists) on the disc
|
||||||
|
func (d *DBStruct) GetTitles(flags uint8, minTitleLength uint32) (uint32, error) {
|
||||||
|
numTitles := C.bd_get_titles(d.bd, C.uint8_t(flags), C.uint32_t(minTitleLength))
|
||||||
|
if numTitles == 0 {
|
||||||
|
return 0, fmt.Errorf("failed to get titles or no titles found")
|
||||||
|
}
|
||||||
|
return uint32(numTitles), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMainTitle 获取主标题
|
||||||
|
// GetMainTitle Get the main title
|
||||||
|
func (d *DBStruct) GetMainTitle() (uint32, error) {
|
||||||
|
mainTitle := C.bd_get_main_title(d.bd)
|
||||||
|
if mainTitle == 0 {
|
||||||
|
return 0, fmt.Errorf("failed to get main title")
|
||||||
|
}
|
||||||
|
return uint32(mainTitle), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTitleInfo 从蓝光光盘获取标题信息 0为全部
|
||||||
|
// GetTitleInfo Get title info from a Blu-ray disc 0 for all
|
||||||
|
func (d *DBStruct) GetTitleInfo(titleIdx uint32, angle uint) (*BlurayTitleInfo, error) {
|
||||||
|
cTitleInfo := C.bd_get_title_info(d.bd, C.uint32_t(titleIdx), C.uint(angle))
|
||||||
|
if cTitleInfo == nil {
|
||||||
|
return nil, fmt.Errorf("failed to get title info")
|
||||||
|
}
|
||||||
|
defer C.bd_free_title_info(cTitleInfo)
|
||||||
|
|
||||||
|
titleInfo := &BlurayTitleInfo{
|
||||||
|
Idx: uint32(cTitleInfo.idx),
|
||||||
|
Playlist: uint32(cTitleInfo.playlist),
|
||||||
|
Duration: uint64(cTitleInfo.duration),
|
||||||
|
ClipCount: uint32(cTitleInfo.clip_count),
|
||||||
|
AngleCount: uint8(cTitleInfo.angle_count),
|
||||||
|
ChapterCount: uint32(cTitleInfo.chapter_count),
|
||||||
|
MarkCount: uint32(cTitleInfo.mark_count),
|
||||||
|
MvcBaseViewRFlag: uint8(cTitleInfo.mvc_base_view_r_flag),
|
||||||
|
}
|
||||||
|
|
||||||
|
if cTitleInfo.clip_count > 0 && cTitleInfo.clips != nil {
|
||||||
|
titleInfo.Clips = make([]BlurayClipInfo, cTitleInfo.clip_count)
|
||||||
|
cClipsSlice := (*[1 << 30]C.BLURAY_CLIP_INFO)(unsafe.Pointer(cTitleInfo.clips))[:cTitleInfo.clip_count:cTitleInfo.clip_count]
|
||||||
|
|
||||||
|
for i := 0; i < int(cTitleInfo.clip_count); i++ {
|
||||||
|
cClip := cClipsSlice[i]
|
||||||
|
|
||||||
|
clipIdStr := C.GoStringN(&cClip.clip_id[0], 5)
|
||||||
|
clipIdStr = strings.TrimLeft(clipIdStr, "0") // Remove leading zeros
|
||||||
|
if clipIdStr == "" {
|
||||||
|
clipIdStr = "0"
|
||||||
|
}
|
||||||
|
clipId, err := strconv.ParseUint(clipIdStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
clipId = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
clipInfo := BlurayClipInfo{
|
||||||
|
PktCount: uint32(cClip.pkt_count),
|
||||||
|
StillMode: uint8(cClip.still_mode),
|
||||||
|
StillTime: uint16(cClip.still_time),
|
||||||
|
VideoStreamCount: uint8(cClip.video_stream_count),
|
||||||
|
AudioStreamCount: uint8(cClip.audio_stream_count),
|
||||||
|
PgStreamCount: uint8(cClip.pg_stream_count),
|
||||||
|
IgStreamCount: uint8(cClip.ig_stream_count),
|
||||||
|
SecAudioStreamCount: uint8(cClip.sec_audio_stream_count),
|
||||||
|
SecVideoStreamCount: uint8(cClip.sec_video_stream_count),
|
||||||
|
StartTime: uint64(cClip.start_time),
|
||||||
|
InTime: uint64(cClip.in_time),
|
||||||
|
OutTime: uint64(cClip.out_time),
|
||||||
|
ClipId: uint(clipId), // Assign the parsed ClipId
|
||||||
|
}
|
||||||
|
|
||||||
|
if cClip.video_stream_count > 0 && cClip.video_streams != nil {
|
||||||
|
clipInfo.VideoStreams = make([]BlurayStreamInfo, cClip.video_stream_count)
|
||||||
|
cVideoStreams := (*[1 << 30]C.BLURAY_STREAM_INFO)(unsafe.Pointer(cClip.video_streams))[:cClip.video_stream_count:cClip.video_stream_count]
|
||||||
|
|
||||||
|
for j := 0; j < int(cClip.video_stream_count); j++ {
|
||||||
|
cStream := cVideoStreams[j]
|
||||||
|
lang := getLanguageCode(cStream.lang)
|
||||||
|
streamInfo := BlurayStreamInfo{
|
||||||
|
CodingType: uint8(cStream.coding_type),
|
||||||
|
Format: uint8(cStream.format),
|
||||||
|
Rate: uint8(cStream.rate),
|
||||||
|
CharCode: uint8(cStream.char_code),
|
||||||
|
Pid: uint16(cStream.pid),
|
||||||
|
Aspect: uint8(cStream.aspect),
|
||||||
|
SubpathId: uint8(cStream.subpath_id),
|
||||||
|
Lang: lang,
|
||||||
|
}
|
||||||
|
clipInfo.VideoStreams[j] = streamInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cClip.audio_stream_count > 0 && cClip.audio_streams != nil {
|
||||||
|
clipInfo.AudioStreams = make([]BlurayStreamInfo, cClip.audio_stream_count)
|
||||||
|
cAudioStreams := (*[1 << 30]C.BLURAY_STREAM_INFO)(unsafe.Pointer(cClip.audio_streams))[:cClip.audio_stream_count:cClip.audio_stream_count]
|
||||||
|
|
||||||
|
for j := 0; j < int(cClip.audio_stream_count); j++ {
|
||||||
|
cStream := cAudioStreams[j]
|
||||||
|
lang := getLanguageCode(cStream.lang)
|
||||||
|
streamInfo := BlurayStreamInfo{
|
||||||
|
CodingType: uint8(cStream.coding_type),
|
||||||
|
Format: uint8(cStream.format),
|
||||||
|
Rate: uint8(cStream.rate),
|
||||||
|
CharCode: uint8(cStream.char_code),
|
||||||
|
Pid: uint16(cStream.pid),
|
||||||
|
Aspect: uint8(cStream.aspect),
|
||||||
|
SubpathId: uint8(cStream.subpath_id),
|
||||||
|
Lang: lang,
|
||||||
|
}
|
||||||
|
clipInfo.AudioStreams[j] = streamInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cClip.pg_stream_count > 0 && cClip.pg_streams != nil {
|
||||||
|
clipInfo.PgStreams = make([]BlurayStreamInfo, cClip.pg_stream_count)
|
||||||
|
cPgStreams := (*[1 << 30]C.BLURAY_STREAM_INFO)(unsafe.Pointer(cClip.pg_streams))[:cClip.pg_stream_count:cClip.pg_stream_count]
|
||||||
|
|
||||||
|
for j := 0; j < int(cClip.pg_stream_count); j++ {
|
||||||
|
cStream := cPgStreams[j]
|
||||||
|
streamInfo := BlurayStreamInfo{
|
||||||
|
CodingType: uint8(cStream.coding_type),
|
||||||
|
Format: uint8(cStream.format),
|
||||||
|
Rate: uint8(cStream.rate),
|
||||||
|
CharCode: uint8(cStream.char_code),
|
||||||
|
Pid: uint16(cStream.pid),
|
||||||
|
Aspect: uint8(cStream.aspect),
|
||||||
|
SubpathId: uint8(cStream.subpath_id),
|
||||||
|
Lang: C.GoStringN((*C.char)(unsafe.Pointer(&cStream.lang[0])), 3),
|
||||||
|
}
|
||||||
|
clipInfo.PgStreams[j] = streamInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cClip.ig_stream_count > 0 && cClip.ig_streams != nil {
|
||||||
|
clipInfo.IgStreams = make([]BlurayStreamInfo, cClip.ig_stream_count)
|
||||||
|
cIgStreams := (*[1 << 30]C.BLURAY_STREAM_INFO)(unsafe.Pointer(cClip.ig_streams))[:cClip.ig_stream_count:cClip.ig_stream_count]
|
||||||
|
|
||||||
|
for j := 0; j < int(cClip.ig_stream_count); j++ {
|
||||||
|
cStream := cIgStreams[j]
|
||||||
|
streamInfo := BlurayStreamInfo{
|
||||||
|
CodingType: uint8(cStream.coding_type),
|
||||||
|
Format: uint8(cStream.format),
|
||||||
|
Rate: uint8(cStream.rate),
|
||||||
|
CharCode: uint8(cStream.char_code),
|
||||||
|
Pid: uint16(cStream.pid),
|
||||||
|
Aspect: uint8(cStream.aspect),
|
||||||
|
SubpathId: uint8(cStream.subpath_id),
|
||||||
|
Lang: C.GoStringN((*C.char)(unsafe.Pointer(&cStream.lang[0])), 3),
|
||||||
|
}
|
||||||
|
clipInfo.IgStreams[j] = streamInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cClip.sec_audio_stream_count > 0 && cClip.sec_audio_streams != nil {
|
||||||
|
clipInfo.SecAudioStreams = make([]BlurayStreamInfo, cClip.sec_audio_stream_count)
|
||||||
|
cSecAudioStreams := (*[1 << 30]C.BLURAY_STREAM_INFO)(unsafe.Pointer(cClip.sec_audio_streams))[:cClip.sec_audio_stream_count:cClip.sec_audio_stream_count]
|
||||||
|
|
||||||
|
for j := 0; j < int(cClip.sec_audio_stream_count); j++ {
|
||||||
|
cStream := cSecAudioStreams[j]
|
||||||
|
streamInfo := BlurayStreamInfo{
|
||||||
|
CodingType: uint8(cStream.coding_type),
|
||||||
|
Format: uint8(cStream.format),
|
||||||
|
Rate: uint8(cStream.rate),
|
||||||
|
CharCode: uint8(cStream.char_code),
|
||||||
|
Pid: uint16(cStream.pid),
|
||||||
|
Aspect: uint8(cStream.aspect),
|
||||||
|
SubpathId: uint8(cStream.subpath_id),
|
||||||
|
Lang: C.GoStringN((*C.char)(unsafe.Pointer(&cStream.lang[0])), 3),
|
||||||
|
}
|
||||||
|
clipInfo.SecAudioStreams[j] = streamInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cClip.sec_video_stream_count > 0 && cClip.sec_video_streams != nil {
|
||||||
|
clipInfo.SecVideoStreams = make([]BlurayStreamInfo, cClip.sec_video_stream_count)
|
||||||
|
cSecVideoStreams := (*[1 << 30]C.BLURAY_STREAM_INFO)(unsafe.Pointer(cClip.sec_video_streams))[:cClip.sec_video_stream_count:cClip.sec_video_stream_count]
|
||||||
|
|
||||||
|
for j := 0; j < int(cClip.sec_video_stream_count); j++ {
|
||||||
|
cStream := cSecVideoStreams[j]
|
||||||
|
streamInfo := BlurayStreamInfo{
|
||||||
|
CodingType: uint8(cStream.coding_type),
|
||||||
|
Format: uint8(cStream.format),
|
||||||
|
Rate: uint8(cStream.rate),
|
||||||
|
CharCode: uint8(cStream.char_code),
|
||||||
|
Pid: uint16(cStream.pid),
|
||||||
|
Aspect: uint8(cStream.aspect),
|
||||||
|
SubpathId: uint8(cStream.subpath_id),
|
||||||
|
Lang: C.GoStringN((*C.char)(unsafe.Pointer(&cStream.lang[0])), 3),
|
||||||
|
}
|
||||||
|
clipInfo.SecVideoStreams[j] = streamInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
titleInfo.Clips[i] = clipInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cTitleInfo.chapter_count > 0 && cTitleInfo.chapters != nil {
|
||||||
|
titleInfo.Chapters = make([]BlurayTitleChapter, cTitleInfo.chapter_count)
|
||||||
|
cChapters := (*[1 << 30]C.BLURAY_TITLE_CHAPTER)(unsafe.Pointer(cTitleInfo.chapters))[:cTitleInfo.chapter_count:cTitleInfo.chapter_count]
|
||||||
|
|
||||||
|
for i := 0; i < int(cTitleInfo.chapter_count); i++ {
|
||||||
|
cChapter := cChapters[i]
|
||||||
|
chapter := BlurayTitleChapter{
|
||||||
|
Idx: uint32(cChapter.idx),
|
||||||
|
Start: uint64(cChapter.start),
|
||||||
|
Duration: uint64(cChapter.duration),
|
||||||
|
Offset: uint64(cChapter.offset),
|
||||||
|
ClipRef: uint(cChapter.clip_ref),
|
||||||
|
}
|
||||||
|
titleInfo.Chapters[i] = chapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cTitleInfo.mark_count > 0 && cTitleInfo.marks != nil {
|
||||||
|
titleInfo.Marks = make([]BlurayTitleMark, cTitleInfo.mark_count)
|
||||||
|
cMarks := (*[1 << 30]C.BLURAY_TITLE_MARK)(unsafe.Pointer(cTitleInfo.marks))[:cTitleInfo.mark_count:cTitleInfo.mark_count]
|
||||||
|
|
||||||
|
for i := 0; i < int(cTitleInfo.mark_count); i++ {
|
||||||
|
cMark := cMarks[i]
|
||||||
|
mark := BlurayTitleMark{
|
||||||
|
Idx: uint32(cMark.idx),
|
||||||
|
Type: int(cMark._type), // Use _type because 'type' is a reserved keyword in Go
|
||||||
|
Start: uint64(cMark.start),
|
||||||
|
Duration: uint64(cMark.duration),
|
||||||
|
Offset: uint64(cMark.offset),
|
||||||
|
ClipRef: uint(cMark.clip_ref),
|
||||||
|
}
|
||||||
|
titleInfo.Marks[i] = mark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return titleInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectTitle 从 GetTitles() 创建的列表中选择标题
|
||||||
|
// [未测试] 由copilot生成的代码
|
||||||
|
// [NotTested] Code generated by copilot
|
||||||
|
func (d *DBStruct) SelectTitle(titleIdx uint32) (int, error) {
|
||||||
|
result := C.bd_select_title(d.bd, C.uint32_t(titleIdx))
|
||||||
|
if result != 0 {
|
||||||
|
return 0, fmt.Errorf("failed to select title")
|
||||||
|
}
|
||||||
|
return int(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectPlaylist 选择播放列表
|
||||||
|
// [未测试] 由copilot生成的代码
|
||||||
|
// [NotTested] Code generated by copilot
|
||||||
|
func (d *DBStruct) SelectPlaylist(playlistIdx uint32) (int, error) {
|
||||||
|
result := C.bd_select_playlist(d.bd, C.uint32_t(playlistIdx))
|
||||||
|
if result != 0 {
|
||||||
|
return 0, fmt.Errorf("failed to select playlist")
|
||||||
|
}
|
||||||
|
return int(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentTitle 获取当前标题
|
||||||
|
// [未测试] 由copilot生成的代码
|
||||||
|
// [NotTested] Code generated by copilot
|
||||||
|
func (d *DBStruct) GetCurrentTitle() (uint32, error) {
|
||||||
|
result := C.bd_get_current_title(d.bd)
|
||||||
|
if result == 0 {
|
||||||
|
return 0, fmt.Errorf("failed to get current title")
|
||||||
|
}
|
||||||
|
return uint32(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read Read from currently selected title file, decrypt if possible
|
||||||
|
// [未测试] 由copilot生成的代码
|
||||||
|
// [NotTested] Code generated by copilot
|
||||||
|
func (d *DBStruct) Read() (int, error) {
|
||||||
|
result := C.bd_read(d.bd)
|
||||||
|
if result != 0 {
|
||||||
|
return 0, fmt.Errorf("failed to read")
|
||||||
|
}
|
||||||
|
return int(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Seek Seek to a position in the currently selected title file
|
||||||
|
//// [未测试] 由copilot生成的代码
|
||||||
|
//// [NotTested] Code generated by copilot
|
||||||
|
//func (d *DBStruct) Seek(pos uint64) (int64, error) {
|
||||||
|
// result := C.bd_seek(d.bd, C.uint64_t(pos))
|
||||||
|
// return int64(result), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SeekTime Seek to a time in the currently selected title file
|
||||||
|
//// [未测试] 由copilot生成的代码
|
||||||
|
//// [NotTested] Code generated by copilot
|
||||||
|
//func (d *DBStruct) SeekTime(tick uint64) (int64, error) {
|
||||||
|
// result := C.bd_seek_time(d.bd, C.uint64_t(tick))
|
||||||
|
// return int64(result), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SeekChapter Seek to a chapter in the currently selected title file
|
||||||
|
//// [未测试] 由copilot生成的代码
|
||||||
|
//// [NotTested] Code generated by copilot
|
||||||
|
//func (d *DBStruct) SeekChapter(chapter uint) (int64, error) {
|
||||||
|
// result := C.bd_seek_chapter(d.bd, C.uint(chapter))
|
||||||
|
// return int64(result), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SeekMark Seek to a mark in the currently selected title file
|
||||||
|
//// [未测试] 由copilot生成的代码
|
||||||
|
//// [NotTested] Code generated by copilot
|
||||||
|
//func (d *DBStruct) SeekMark(mark uint) (int64, error) {
|
||||||
|
// result := C.bd_seek_mark(d.bd, C.uint(mark))
|
||||||
|
// return int64(result), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SeekPlayitem Seek to a playitem in the currently selected title file
|
||||||
|
//// [未测试] 由copilot生成的代码
|
||||||
|
//// [NotTested] Code generated by copilot
|
||||||
|
//func (d *DBStruct) SeekPlayitem(clipRef uint) (int64, error) {
|
||||||
|
// result := C.bd_seek_playitem(d.bd, C.uint(clipRef))
|
||||||
|
// return int64(result), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SelectAngle Set the angle to play
|
||||||
|
//// [未测试] 由copilot生成的代码
|
||||||
|
//// [NotTested] Code generated by copilot
|
||||||
|
//func (d *DBStruct) SelectAngle(angle uint) (int, error) {
|
||||||
|
// result := C.bd_select_angle(d.bd, C.uint(angle))
|
||||||
|
// if result != 0 {
|
||||||
|
// return 0, fmt.Errorf("failed to select angle")
|
||||||
|
// }
|
||||||
|
// return int(result), nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SeamlessAngleChange Change the angle without stopping playback
|
||||||
|
//// [未测试] 由copilot生成的代码
|
||||||
|
//// [NotTested] Code generated by copilot
|
||||||
|
//func (d *DBStruct) SeamlessAngleChange(angle uint) {
|
||||||
|
// C.bd_seamless_angle_change(d.bd, C.uint(angle))
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// SelectStream
|
||||||
|
///**
|
||||||
|
// *
|
||||||
|
// * Select stream (PG / TextST track)
|
||||||
|
// *
|
||||||
|
// * When playing with on-disc menus:
|
||||||
|
// *
|
||||||
|
// * Stream selection is controlled by on-disc menus.
|
||||||
|
// * If user can change stream selection also in player GUI, this function
|
||||||
|
// * should be used to keep on-disc menus in sync with player GUI.
|
||||||
|
// *
|
||||||
|
// * When playing the disc without on-disc menus:
|
||||||
|
// *
|
||||||
|
// * Initial stream selection is done using preferred language settings.
|
||||||
|
// * This function can be used to override automatic stream selection.
|
||||||
|
// * Without on-disc menus selecting the stream is useful only when using
|
||||||
|
// * libbluray internal decoders or the stream is stored in a sub-path.
|
||||||
|
// *
|
||||||
|
// * @param bd BLURAY object
|
||||||
|
// * @param stream_type BLURAY_AUDIO_STREAM or BLURAY_PG_TEXTST_STREAM
|
||||||
|
// * @param stream_id stream number (1..N)
|
||||||
|
// * @param enable_flag set to 0 to disable streams of this type
|
||||||
|
// */
|
||||||
|
//func (d *DBStruct) SelectStream(streamType uint32, streamID uint32, enableFlag uint32) {
|
||||||
|
// C.bd_select_stream(d.bd, C.uint32_t(streamType), C.uint32_t(streamID), C.uint32_t(enableFlag))
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//// Close 关闭蓝光光盘
|
||||||
|
//func (d *DBStruct) Close() {
|
||||||
|
// C.bd_close(d.bd)
|
||||||
|
//}
|
||||||
140
libbluray_type.go
Normal file
140
libbluray_type.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
package bluray
|
||||||
|
|
||||||
|
// BlurayTitle 表示BluRay标题的信息
|
||||||
|
type BlurayTitle struct {
|
||||||
|
Name *string // 可选的标题名称,使用首选语言
|
||||||
|
Interactive uint8 // 如果标题是交互式的,值为1(在UI中不显示标题长度和播放位置)
|
||||||
|
Accessible uint8 // 如果允许跳转到该标题,值为1
|
||||||
|
Hidden uint8 // 如果在播放期间不显示标题编号,值为1
|
||||||
|
|
||||||
|
BDJ uint8 // 0表示HDMV标题,1表示BD-J标题
|
||||||
|
IDRef uint32 // 电影对象编号/bdjo文件编号
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlurayDiscInfo 表示BluRay光盘的信息
|
||||||
|
type BlurayDiscInfo struct {
|
||||||
|
BlurayDetected uint8 // 如果检测到BluRay光盘,值为1
|
||||||
|
|
||||||
|
// 光盘ID
|
||||||
|
DiscName *string // 可选的光盘名称,使用首选语言
|
||||||
|
UDFVolumeID *string // 可选的UDF卷标符
|
||||||
|
DiscID [20]byte // 光盘ID
|
||||||
|
|
||||||
|
// HDMV / BD-J 标题
|
||||||
|
NoMenuSupport uint8 // 如果无法通过光盘上的菜单播放该光盘,值为1
|
||||||
|
FirstPlaySupported uint8 // 如果光盘上有First Play标题且可以播放,值为1
|
||||||
|
TopMenuSupported uint8 // 如果光盘上有Top Menu标题且可以播放,值为1
|
||||||
|
|
||||||
|
NumTitles uint32 // 光盘上的标题数量,不包括"First Play"和"Top Menu"
|
||||||
|
Titles []*BlurayTitle // 标题数组,索引为标题编号1...N
|
||||||
|
FirstPlay *BlurayTitle // titles[N+1],如果光盘上不存在则为NULL
|
||||||
|
TopMenu *BlurayTitle // titles[0],如果光盘上不存在则为NULL
|
||||||
|
|
||||||
|
NumHDMVTitles uint32 // HDMV标题数量
|
||||||
|
NumBDJTitles uint32 // BD-J标题数量
|
||||||
|
NumUnsupportedTitles uint32 // 不支持的标题数量
|
||||||
|
|
||||||
|
// BD-J 信息(仅当光盘使用BD-J时有效)
|
||||||
|
BDJDetected uint8 // 如果光盘使用BD-J,值为1
|
||||||
|
BDJSupported uint8 // (已弃用)
|
||||||
|
LibJVMDetected uint8 // 如果找到可用的Java虚拟机,值为1
|
||||||
|
BDJHandled uint8 // 如果找到可用的Java虚拟机和libbluray.jar,值为1
|
||||||
|
|
||||||
|
BDJOrgID [9]byte // (BD-J)光盘组织ID
|
||||||
|
BDJDiscID [33]byte // (BD-J)光盘ID
|
||||||
|
|
||||||
|
// 光盘应用程序信息
|
||||||
|
VideoFormat uint8 // 视频格式(bd_video_format_e)
|
||||||
|
FrameRate uint8 // 帧率(bd_video_rate_e)
|
||||||
|
ContentExist3D uint8 // 如果光盘上存在3D内容,值为1
|
||||||
|
InitialOutputModePreference uint8 // 0表示2D,1表示3D
|
||||||
|
ProviderData [32]byte // 内容提供者数据
|
||||||
|
|
||||||
|
// AACS 信息(仅当光盘使用AACS时有效)
|
||||||
|
AACDetected uint8 // 如果光盘使用AACS加密,值为1
|
||||||
|
LibAACSDetected uint8 // 如果找到可用的AACS解码库,值为1
|
||||||
|
AACSHandled uint8 // 如果光盘使用支持的AACS加密,值为1
|
||||||
|
|
||||||
|
AACSErrorCode int // AACS错误代码(BD_AACS_)
|
||||||
|
AACSMKBV int // AACS MKB版本
|
||||||
|
|
||||||
|
// BD+ 信息(仅当光盘使用BD+时有效)
|
||||||
|
BDPlusDetected uint8 // 如果光盘使用BD+加密,值为1
|
||||||
|
LibBDPlusDetected uint8 // 如果找到可用的BD+解码库,值为1
|
||||||
|
BDPlusHandled uint8 // 如果光盘使用支持的BD+加密,值为1
|
||||||
|
|
||||||
|
BDPlusGen uint8 // BD+内容代码生成
|
||||||
|
BDPlusDate uint32 // BD+内容代码发布日期 ((year<<16)|(month<<8)|day)
|
||||||
|
|
||||||
|
// 光盘应用程序信息(libbluray > 1.2.0)
|
||||||
|
InitialDynamicRangeType uint8 // 动态范围类型(bd_dynamic_range_type_e)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlurayMetaFile struct {
|
||||||
|
Data []byte
|
||||||
|
Size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlurayStreamInfo struct {
|
||||||
|
CodingType uint8
|
||||||
|
Format uint8
|
||||||
|
Rate uint8
|
||||||
|
CharCode uint8
|
||||||
|
Lang string
|
||||||
|
Pid uint16
|
||||||
|
Aspect uint8
|
||||||
|
SubpathId uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlurayClipInfo struct {
|
||||||
|
PktCount uint32
|
||||||
|
StillMode uint8
|
||||||
|
StillTime uint16
|
||||||
|
VideoStreamCount uint8
|
||||||
|
AudioStreamCount uint8
|
||||||
|
PgStreamCount uint8
|
||||||
|
IgStreamCount uint8
|
||||||
|
SecAudioStreamCount uint8
|
||||||
|
SecVideoStreamCount uint8
|
||||||
|
VideoStreams []BlurayStreamInfo
|
||||||
|
AudioStreams []BlurayStreamInfo
|
||||||
|
PgStreams []BlurayStreamInfo
|
||||||
|
IgStreams []BlurayStreamInfo
|
||||||
|
SecAudioStreams []BlurayStreamInfo
|
||||||
|
SecVideoStreams []BlurayStreamInfo
|
||||||
|
StartTime uint64
|
||||||
|
InTime uint64
|
||||||
|
OutTime uint64
|
||||||
|
ClipId uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlurayTitleChapter struct {
|
||||||
|
Idx uint32
|
||||||
|
Start uint64
|
||||||
|
Duration uint64
|
||||||
|
Offset uint64
|
||||||
|
ClipRef uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlurayTitleMark struct {
|
||||||
|
Idx uint32
|
||||||
|
Type int
|
||||||
|
Start uint64
|
||||||
|
Duration uint64
|
||||||
|
Offset uint64
|
||||||
|
ClipRef uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlurayTitleInfo struct {
|
||||||
|
Idx uint32
|
||||||
|
Playlist uint32
|
||||||
|
Duration uint64
|
||||||
|
ClipCount uint32
|
||||||
|
AngleCount uint8
|
||||||
|
ChapterCount uint32
|
||||||
|
MarkCount uint32
|
||||||
|
Clips []BlurayClipInfo
|
||||||
|
Chapters []BlurayTitleChapter
|
||||||
|
Marks []BlurayTitleMark
|
||||||
|
MvcBaseViewRFlag uint8
|
||||||
|
}
|
||||||
29
tools.go
Normal file
29
tools.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package bluray
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdint.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func cStringToGoString(cStr *C.char) *string {
|
||||||
|
if cStr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
str := C.GoString(cStr)
|
||||||
|
return &str
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLanguageCode(langField [4]C.uint8_t) string {
|
||||||
|
isEmpty := true
|
||||||
|
for _, b := range langField {
|
||||||
|
if b != 0 {
|
||||||
|
isEmpty = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isEmpty {
|
||||||
|
return "und"
|
||||||
|
}
|
||||||
|
return C.GoStringN((*C.char)(unsafe.Pointer(&langField[0])), 3)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user