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