All Products
Search
Document Center

ApsaraVideo Live:Demo Pemanggilan API Switcher Serbaguna

Last Updated:Mar 07, 2026

Dokumen ini menyediakan demo lengkap yang dapat dijalankan untuk mengelola studio produksi dalam mode umum menggunakan OpenAPI.

Siklus hidup studio produksi

Siklus hidup switcher studio produksi mode umum adalah sebagai berikut:

Penting

Tambahkan sumber video sebelum menambahkan tata letak. Jika tidak, tata letak tidak akan diterapkan.

  1. Buat studio produksi

  2. Konfigurasikan studio produksi

  3. Tambahkan sumber video

  4. Tambahkan tata letak

  5. Atur adegan pemutaran

  6. (Opsional) Tambahkan komponen

    Anda dapat menambahkan komponen seperti subtitel real-time, gambar, dan teks. Langkah ini dapat dilewati.

  7. Mulai studio produksi

  8. Hentikan studio produksi

  9. Hapus Caster

Kode contoh

Catatan
  • Buat Studio Produksi

    // Buat studio produksi.
    func CreateCaster(liveClient *live20161101.Client) (*live20161101.CreateCasterResponse, error) {
    	// Parameter permintaan untuk membuat studio produksi.
    	createCasterRequest := &live20161101.CreateCasterRequest{}
    	createCasterRequest.ClientToken = tea.String(uuid.New().String())
    	createCasterRequest.CasterName = tea.String("Studio produksi untuk pengujian")
    	createCasterRequest.ChargeType = tea.String("PostPaid")
    	createCasterRequest.NormType = tea.Int32(1)
    	createCasterResponse, err := liveClient.CreateCaster(createCasterRequest)
    	if err != nil {
    		return nil, errors.New("CreateCaster gagal: " + err.Error())
    	}
    	return createCasterResponse, nil
    }

    Untuk informasi lebih lanjut tentang parameter permintaan operasi CreateCaster, lihat CreateCaster.

  • Konfigurasikan studio produksi

    // Konfigurasikan studio produksi.
    func SetCasterConfig(liveClient *live20161101.Client, casterId *string, domainName *string) (*live20161101.SetCasterConfigResponse, error) {
    	// Parameter untuk mengonfigurasi studio produksi.
    	createSetCasterConfig := &live20161101.SetCasterConfigRequest{}
    	createSetCasterConfig.CasterId = casterId
    	createSetCasterConfig.CasterName = tea.String("Studio produksi untuk pengujian")
    	createSetCasterConfig.ChannelEnable = tea.Int32(1)
    	createSetCasterConfig.Delay = tea.Float32(0)
    	createSetCasterConfig.DomainName = domainName
    	createSetCasterConfig.ProgramEffect = tea.Int32(1)
    	createSetCasterConfig.ProgramName = tea.String("pemutaran loop uji coba")
    	// Pengaturan transcoding (orientasi layar dan definisi).
    	// Parameter CasterTemplate dapat diatur ke nilai berikut: lp_ld (definisi rendah), lp_sd (definisi standar), lp_hd (definisi tinggi), atau lp_ud (resolusi ultra-tinggi). lp_ld_v (definisi rendah dalam mode potret), lp_sd_v (definisi standar dalam mode potret), lp_hd_v (definisi tinggi dalam mode potret), atau lp_ud_v (resolusi ultra-tinggi dalam mode potret).
    	createSetCasterConfig.TranscodeConfig = tea.String(`{"CasterTemplate": "lp_ld"}`)
    	// Konfigurasikan parameter perekaman untuk studio produksi. Untuk informasi lebih lanjut tentang bidang JSON, lihat AddLiveAppRecordConfig.
    	// createSetCasterConfig.RecordConfig = tea.String(`{ "endpoint": "", "ossBucket": "", "videoFormat": [{"format": "flv", "interval": 900, "prefix":"record/{AppName}/{StreamName}/{StartTime}_{EndTime}" }]}`)
    	setCasterConfigResponse, err := liveClient.SetCasterConfig(createSetCasterConfig)
    	if err != nil {
    		return nil, errors.New("SetCasterConfig gagal: " + err.Error())
    	}
    	return setCasterConfigResponse, nil
    }

    Untuk informasi lebih lanjut tentang operasi SetCasterConfig, lihat Konfigurasikan studio produksi.

  • Tambahkan Sumber video dan ikatkan ke saluran

    // Tambahkan sumber video.
    func AddCasterVideoResource(liveClient *live20161101.Client, casterId *string) error {
    	// Buat sumber video untuk studio produksi.
    	addCasterVideoResourceRequest := &live20161101.AddCasterVideoResourceRequest{}
    	addCasterVideoResourceRequest.CasterId = casterId
    	addCasterVideoResourceRequest.ResourceName = tea.String("Sumber video untuk pengujian")
    	addCasterVideoResourceRequest.LiveStreamUrl = tea.String("xxxxx")
    	addCasterVideoResourceRequest.PtsCallbackInterval = tea.Int32(1000)
    	addCasterVideoResourceResp, err := liveClient.AddCasterVideoResource(addCasterVideoResourceRequest)
    	if err != nil {
    		return errors.New("AddCasterVideoResource gagal: " + err.Error())
    	}
    	resourceId := addCasterVideoResourceResp.Body.ResourceId
    
    	// Bind sumber video ke channel.
    	setCasterChannelRequest := &live20161101.SetCasterChannelRequest{}
    	setCasterChannelRequest.CasterId = casterId
    	setCasterChannelRequest.PlayStatus = tea.Int32(1)
    	setCasterChannelRequest.ChannelId = tea.String("RV01")
    	setCasterChannelRequest.ResourceId = resourceId
    	_, err = liveClient.SetCasterChannel(setCasterChannelRequest)
    	if err != nil {
    		return errors.New("SetCasterChannel gagal: " + err.Error())
    	}
    	return nil
    }

    Untuk informasi lebih lanjut, lihat AddCasterVideoResource.

  • Tambahkan tata letak

    // Tambahkan tata letak.
    func AddCasterLayout(liveClient *live20161101.Client, casterId *string) (*live20161101.AddCasterLayoutResponse, error) {
    	// Buat tata letak.
    	addCasterLayoutRequest := &live20161101.AddCasterLayoutRequest{}
    	addCasterLayoutRequest.CasterId = casterId
    	addCasterLayoutRequest.BlendList = []*string{tea.String("RV01")} // Posisi video.
    	addCasterLayoutRequest.MixList = []*string{tea.String("RV01")}   // Posisi video.
    
    	audioLayer := &live20161101.AddCasterLayoutRequestAudioLayer{}
    	audioLayer.VolumeRate = tea.Float32(1)
    	audioLayer.ValidChannel = tea.String("all")
    	audioLayer.FixedDelayDuration = tea.Int32(0)
    
    	addCasterLayoutRequest.AudioLayer = []*live20161101.AddCasterLayoutRequestAudioLayer{
    		audioLayer,
    	}
    
    	videoLayer := &live20161101.AddCasterLayoutRequestVideoLayer{
    		FillMode:           tea.String("fit"),
    		HeightNormalized:   tea.Float32(1),
    		WidthNormalized:    tea.Float32(1),
    		PositionRefer:      tea.String("topLeft"),
    		PositionNormalized: []*float32{tea.Float32(0), tea.Float32(0)},
    		FixedDelayDuration: tea.Int32(0),
    	}
    
    	addCasterLayoutRequest.VideoLayer = []*live20161101.AddCasterLayoutRequestVideoLayer{
    		videoLayer,
    	}
    	addCasterLayoutResponse, err := liveClient.AddCasterLayout(addCasterLayoutRequest)
    	if err != nil {
    		return nil, errors.New("AddCasterLayout gagal: " + err.Error())
    	}
    	return addCasterLayoutResponse, nil
    }

    Untuk informasi lebih lanjut, lihat AddCasterLayout.

  • Mulai studio produksi dan atur adegan pemutaran

    // Mulai studio produksi dan atur adegan pemutaran.
    func StartCaster(liveClient *live20161101.Client, casterId *string, layoutId *string) (*live20161101.StartCasterResponse, error) {
    	// Mulai studio produksi.
    	startCasterRequest := &live20161101.StartCasterRequest{
    		CasterId: casterId,
    	}
    	startCasterResp, err := liveClient.StartCaster(startCasterRequest)
    	if err != nil {
    		return nil, errors.New("StartCaster gagal: " + err.Error())
    	}
    
    	// Membutuhkan waktu untuk memuat sumber daya. Jika Anda langsung memulai studio produksi, sumber daya mungkin gagal dimuat. Oleh karena itu, jeda selama satu detik.
    	time.Sleep(time.Second)
    
    	// Atur adegan pemutaran.
    	// PVW (Opsional)
    	setCasterSceneConfigRequest := &live20161101.SetCasterSceneConfigRequest{
    		CasterId: casterId,
    		LayoutId: layoutId,
    		SceneId:  startCasterResp.Body.PvwSceneInfos.SceneInfo[0].SceneId,
    	}
    
    	_, err = liveClient.SetCasterSceneConfig(setCasterSceneConfigRequest)
    	if err != nil {
    		return nil, errors.New("SetCasterSceneConfig gagal: " + err.Error())
    	}
    	// PGM (Wajib)
    	setCasterSceneConfigRequest.SceneId = startCasterResp.Body.PgmSceneInfos.SceneInfo[0].SceneId
    	_, err = liveClient.SetCasterSceneConfig(setCasterSceneConfigRequest)
    	if err != nil {
    		return nil, errors.New("SetCasterSceneConfig gagal: " + err.Error())
    	}
    	return startCasterResp, nil
    }

    Untuk informasi lebih lanjut tentang operasi StartCaster, lihat StartCaster.

    Untuk informasi lebih lanjut tentang operasi SetCasterSceneConfig, lihat Atur konfigurasi adegan.

  • Tambahkan komponen subtitel real-time dan terapkan ke adegan

    // Tambahkan komponen subtitel real-time dan terapkan ke adegan.
    func AddETComponent(liveClient *live20161101.Client, casterId *string, layoutId *string, pgmSceneId *string) error {
    	// Prasyarat: Studio produksi telah dibuat, aliran langsung ditambahkan sebagai sumber video, dan locationID adalah RV01.
    	// Catatan: Subtitel real-time hanya berlaku untuk aliran langsung.
    	// Buat komponen.
    	r := &live20161101.AddCasterComponentRequest{
    		CasterId:       casterId,
    		ComponentType:  tea.String("caption"),
    		ComponentName:  tea.String("Subtitel real-time untuk pengujian"),
    		Effect:         tea.String("none"),
    		LocationId:     tea.String("RV01"),
    		ComponentLayer: tea.String(`{"HeightNormalized":"1","WidthNormalized":"1","PositionRefer":"topLeft","PositionNormalized":["0.05", "0.7"]}`),
    		// Untuk informasi lebih lanjut tentang bidang caption, lihat deskripsi parameter CaptionLayerContent dalam topik AddCasterComponent.
    		// Bidang-bidang berikut ditambahkan. Perhatikan bahwa dokumentasi resmi belum diperbarui.
    		// BoxWidthNormalized: Lebar ternormalisasi latar belakang teks. Nilai ini dihitung berdasarkan ukuran font, yaitu boxWidth/font_size. Jika nilai yang dihitung melebihi 16, maka digunakan nilai 16. Nilai default: 0.
    		// BoxColor: Warna latar belakang teks. Nilainya dalam format 0xRGBA. Misalnya, 0xff0000ff menunjukkan merah buram. Nilai default adalah "", yang berarti parameter ini tidak valid.
    		// ShadowxWidthNormalized: Nilai ternormalisasi koordinat x bayangan teks. Nilai ini dihitung berdasarkan ukuran font, yaitu shadowxWidth/font_size. Jika nilai yang dihitung melebihi 16, maka digunakan nilai 16. Nilai default: 0.
    		// ShadowyWidthNormalized: Nilai ternormalisasi koordinat y bayangan teks. Nilai ini dihitung berdasarkan ukuran font, yaitu shadowyWidth/font_size. Jika nilai yang dihitung melebihi 16, maka digunakan nilai 16. Nilai default: 0.
    		// ShadowColor: Warna bayangan teks. Nilainya dalam format 0xRGBA. Misalnya, 0xff0000ff menunjukkan merah buram. Nilai default adalah "", yang berarti parameter ini tidak valid.
    		CaptionLayerContent: tea.String(`{
            "SizeNormalized": 0.05,
            "Color": "0xFFFFFF",
            "LocationId": "RV01",
            "BorderColor": "0x696969",
            "BorderWidthNormalized": 0.1,
            "BoxColor": "0xffffff",
            "BoxWidthNormalized": 0.7,
            "ShadowColor": "0x3c3c3c",
            "ShadowxWidthNormalized": 0.4,
            "ShadowyWidthNormalized": 0.4,
            "SourceLan": "cn",
            "TargetLan": "en",
            "PtsOffset": -1000,
            "SourceLanPerLineWordCount": 28,
            "TargetLanPerLineWordCount": 60,
            "ShowSourceLan": true,
            "ShowTargetLan": true,
            "Truncation": false,
            "AppearDuration": 20000,
            "AppearMode": "Movie"
            }`),
    	}
    
    	addCasterComponentResp, err := liveClient.AddCasterComponent(r)
    	if err != nil {
    		return errors.New("AddCasterComponent gagal: " + err.Error())
    	}
    
    	// Terapkan komponen ke adegan yang ditentukan.
    	setCasterSceneConfigRequest := &live20161101.UpdateCasterSceneConfigRequest{
    		CasterId:    casterId,
    		LayoutId:    layoutId,
    		SceneId:     pgmSceneId,
    		ComponentId: []*string{addCasterComponentResp.Body.ComponentId},
    	}
    
    	_, err = liveClient.UpdateCasterSceneConfig(setCasterSceneConfigRequest)
    	if err != nil {
    		return errors.New("UpdateCasterSceneConfig gagal: " + err.Error())
    	}
    	return nil
    }

    Untuk informasi lebih lanjut tentang operasi AddCasterComponent, lihat AddCasterComponent.

    Untuk informasi lebih lanjut tentang antarmuka SetCasterSceneConfig, lihat Atur Konfigurasi Adegan.

  • Ganti tata letak

    // Ganti tata letak.
    func ChangeLayout(liveClient *live20161101.Client, casterId *string, pgmSceneId *string, newLayoutId *string) error {
    	// Ubah ID tata letak adegan.
    	setCasterSceneConfigRequest := &live20161101.UpdateCasterSceneConfigRequest{
    		CasterId: casterId,
    		SceneId:  pgmSceneId,
    		LayoutId: newLayoutId,
    	}
    	_, err := liveClient.UpdateCasterSceneConfig(setCasterSceneConfigRequest)
    	if err != nil {
    		return errors.New("UpdateCasterSceneConfig gagal: " + err.Error())
    	}
    	return nil
    }

    Untuk informasi lebih lanjut tentang operasi SetCasterSceneConfig, lihat SetCasterSceneConfig.

  • Hentikan studio produksi

    // Hentikan studio produksi.
    func StopCaster(liveClient *live20161101.Client, casterId *string) error {
    	stopCasterRequest := &live20161101.StopCasterRequest{
    		CasterId: casterId,
    	}
    	_, err := liveClient.StopCaster(stopCasterRequest)
    	if err != nil {
    		return errors.New("StopCaster gagal: " + err.Error())
    	}
    	return nil
    }

    Untuk informasi lebih lanjut tentang operasi StopCaster, lihat StopCaster.

  • Hapus studio produksi

    // Hapus studio produksi.
    func DeleteCaster(liveClient *live20161101.Client, casterId *string) error {
    	deleteCasterRequest := &live20161101.DeleteCasterRequest{
    		CasterId: casterId,
    	}
    	_, err := liveClient.DeleteCaster(deleteCasterRequest)
    	if err != nil {
    		return errors.New("DeleteCaster gagal: " + err.Error())
    	}
    	return nil
    }

    Untuk informasi lebih lanjut tentang operasi DeleteCaster, lihat DeleteCaster.

package main

import (
	"errors"
	"log"
	"sync"
	"time"

	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	live20161101 "github.com/alibabacloud-go/live-20161101/v2/client"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/google/uuid"
	"gopkg.in/ini.v1"
)

type LiveClient struct {
	instance *live20161101.Client
	once     sync.Once
}

var liveClientInstance = &LiveClient{}

func GetClientInstance() (*live20161101.Client, error) {
	section, err := getAccessKey()
	if err != nil {
		return nil, err
	}
	liveClientInstance.once.Do(func() {
		cfg := &openapi.Config{
			Endpoint:        tea.String("live.aliyuncs.com"),
			AccessKeyId:     tea.String(section.Key("access_key_id").String()),
			AccessKeySecret: tea.String(section.Key("access_key_secret").String()),
		}

		liveClientInstance.instance, err = live20161101.NewClient(cfg)
		if err != nil {
			err = errors.New("Inisialisasi client Live gagal: " + err.Error())
		}
	})
	if err != nil {
		return nil, err
	}
	return liveClientInstance.instance, nil
}

func getAccessKey() (*ini.Section, error) {
	cfg, err := ini.Load("conf/config.ini")
	if err != nil {
		return nil, errors.New("ERROR: Tidak dapat membuka file " + err.Error())
	}
	section, err := cfg.GetSection("")
	if err != nil {
		return nil, errors.New("ERROR: Tidak dapat menemukan bagian " + err.Error())
	}
	return section, nil
}

// Buat studio produksi.
func CreateCaster(liveClient *live20161101.Client) (*live20161101.CreateCasterResponse, error) {
	// Parameter permintaan untuk membuat studio produksi.
	createCasterRequest := &live20161101.CreateCasterRequest{}
	createCasterRequest.ClientToken = tea.String(uuid.New().String())
	createCasterRequest.CasterName = tea.String("Studio produksi untuk pengujian")
	createCasterRequest.ChargeType = tea.String("PostPaid")
	createCasterRequest.NormType = tea.Int32(1)
	createCasterResponse, err := liveClient.CreateCaster(createCasterRequest)
	if err != nil {
		return nil, errors.New("CreateCaster gagal: " + err.Error())
	}
	return createCasterResponse, nil
}

// Konfigurasikan studio produksi.
func SetCasterConfig(liveClient *live20161101.Client, casterId *string, domainName *string) (*live20161101.SetCasterConfigResponse, error) {
	// Parameter untuk mengonfigurasi studio produksi.
	createSetCasterConfig := &live20161101.SetCasterConfigRequest{}
	createSetCasterConfig.CasterId = casterId
	createSetCasterConfig.CasterName = tea.String("Studio produksi untuk pengujian")
	createSetCasterConfig.ChannelEnable = tea.Int32(1)
	createSetCasterConfig.Delay = tea.Float32(0)
	createSetCasterConfig.DomainName = domainName
	createSetCasterConfig.ProgramEffect = tea.Int32(1)
	createSetCasterConfig.ProgramName = tea.String("pemutaran loop uji coba")
	// Pengaturan transcoding (orientasi layar dan definisi).
	// Parameter CasterTemplate dapat diatur ke nilai berikut: lp_ld (definisi rendah), lp_sd (definisi standar), lp_hd (definisi tinggi), atau lp_ud (resolusi ultra-tinggi). lp_ld_v (definisi rendah dalam mode potret), lp_sd_v (definisi standar dalam mode potret), lp_hd_v (definisi tinggi dalam mode potret), atau lp_ud_v (resolusi ultra-tinggi dalam mode potret).
	createSetCasterConfig.TranscodeConfig = tea.String(`{"CasterTemplate": "lp_ld"}`)
	// Konfigurasikan parameter perekaman untuk studio produksi. Untuk informasi lebih lanjut tentang bidang JSON, lihat AddLiveAppRecordConfig.
	// createSetCasterConfig.RecordConfig = tea.String(`{ "endpoint": "", "ossBucket": "", "videoFormat": [{"format": "flv", "interval": 900, "prefix":"record/{AppName}/{StreamName}/{StartTime}_{EndTime}" }]}`)
	setCasterConfigResponse, err := liveClient.SetCasterConfig(createSetCasterConfig)
	if err != nil {
		return nil, errors.New("SetCasterConfig gagal: " + err.Error())
	}
	return setCasterConfigResponse, nil
}

// Tambahkan sumber video.
func AddCasterVideoResource(liveClient *live20161101.Client, casterId *string) error {
	// Buat sumber video untuk studio produksi.
	addCasterVideoResourceRequest := &live20161101.AddCasterVideoResourceRequest{}
	addCasterVideoResourceRequest.CasterId = casterId
	addCasterVideoResourceRequest.ResourceName = tea.String("Sumber video untuk pengujian")
	addCasterVideoResourceRequest.LiveStreamUrl = tea.String("xxxxx")
	addCasterVideoResourceRequest.PtsCallbackInterval = tea.Int32(1000)
	addCasterVideoResourceResp, err := liveClient.AddCasterVideoResource(addCasterVideoResourceRequest)
	if err != nil {
		return errors.New("AddCasterVideoResource gagal: " + err.Error())
	}
	resourceId := addCasterVideoResourceResp.Body.ResourceId

	// Bind sumber video ke channel.
	setCasterChannelRequest := &live20161101.SetCasterChannelRequest{}
	setCasterChannelRequest.CasterId = casterId
	setCasterChannelRequest.PlayStatus = tea.Int32(1)
	setCasterChannelRequest.ChannelId = tea.String("RV01")
	setCasterChannelRequest.ResourceId = resourceId
	_, err = liveClient.SetCasterChannel(setCasterChannelRequest)
	if err != nil {
		return errors.New("SetCasterChannel gagal: " + err.Error())
	}
	return nil
}

// Tambahkan tata letak.
func AddCasterLayout(liveClient *live20161101.Client, casterId *string) (*live20161101.AddCasterLayoutResponse, error) {
	// Buat tata letak.
	addCasterLayoutRequest := &live20161101.AddCasterLayoutRequest{}
	addCasterLayoutRequest.CasterId = casterId
	addCasterLayoutRequest.BlendList = []*string{tea.String("RV01")} // Posisi video.
	addCasterLayoutRequest.MixList = []*string{tea.String("RV01")}   // Posisi video.

	audioLayer := &live20161101.AddCasterLayoutRequestAudioLayer{}
	audioLayer.VolumeRate = tea.Float32(1)
	audioLayer.ValidChannel = tea.String("all")
	audioLayer.FixedDelayDuration = tea.Int32(0)

	addCasterLayoutRequest.AudioLayer = []*live20161101.AddCasterLayoutRequestAudioLayer{
		audioLayer,
	}

	videoLayer := &live20161101.AddCasterLayoutRequestVideoLayer{
		FillMode:           tea.String("fit"),
		HeightNormalized:   tea.Float32(1),
		WidthNormalized:    tea.Float32(1),
		PositionRefer:      tea.String("topLeft"),
		PositionNormalized: []*float32{tea.Float32(0), tea.Float32(0)},
		FixedDelayDuration: tea.Int32(0),
	}

	addCasterLayoutRequest.VideoLayer = []*live20161101.AddCasterLayoutRequestVideoLayer{
		videoLayer,
	}
	addCasterLayoutResponse, err := liveClient.AddCasterLayout(addCasterLayoutRequest)
	if err != nil {
		return nil, errors.New("AddCasterLayout gagal: " + err.Error())
	}
	return addCasterLayoutResponse, nil
}

// Tambahkan komponen subtitel real-time dan terapkan ke adegan.
func AddETComponent(liveClient *live20161101.Client, casterId *string, layoutId *string, pgmSceneId *string) error {
	// Prasyarat: Studio produksi telah dibuat, aliran langsung ditambahkan sebagai sumber video, dan locationID adalah RV01.
	// Catatan: Subtitel real-time hanya berlaku untuk aliran langsung.
	// Buat komponen.
	r := &live20161101.AddCasterComponentRequest{
		CasterId:       casterId,
		ComponentType:  tea.String("caption"),
		ComponentName:  tea.String("Subtitel real-time untuk pengujian"),
		Effect:         tea.String("none"),
		LocationId:     tea.String("RV01"),
		ComponentLayer: tea.String(`{"HeightNormalized":"1","WidthNormalized":"1","PositionRefer":"topLeft","PositionNormalized":["0.05", "0.7"]}`),
		// Untuk informasi lebih lanjut tentang bidang caption, lihat deskripsi parameter CaptionLayerContent dalam topik AddCasterComponent.
		// Bidang-bidang berikut ditambahkan. Perhatikan bahwa dokumentasi resmi belum diperbarui.
		// BoxWidthNormalized: Lebar ternormalisasi latar belakang teks. Nilai ini dihitung berdasarkan ukuran font, yaitu boxWidth/font_size. Jika nilai yang dihitung melebihi 16, maka digunakan nilai 16. Nilai default: 0.
		// BoxColor: Warna latar belakang teks. Nilainya dalam format 0xRGBA. Misalnya, 0xff0000ff menunjukkan merah buram. Nilai default adalah "", yang berarti parameter ini tidak valid.
		// ShadowxWidthNormalized: Nilai ternormalisasi koordinat x bayangan teks. Nilai ini dihitung berdasarkan ukuran font, yaitu shadowxWidth/font_size. Jika nilai yang dihitung melebihi 16, maka digunakan nilai 16. Nilai default: 0.
		// ShadowyWidthNormalized: Nilai ternormalisasi koordinat y bayangan teks. Nilai ini dihitung berdasarkan ukuran font, yaitu shadowyWidth/font_size. Jika nilai yang dihitung melebihi 16, maka digunakan nilai 16. Nilai default: 0.
		// ShadowColor: Warna bayangan teks. Nilainya dalam format 0xRGBA. Misalnya, 0xff0000ff menunjukkan merah buram. Nilai default adalah "", yang berarti parameter ini tidak valid.
		CaptionLayerContent: tea.String(`{
        "SizeNormalized": 0.05,
        "Color": "0xFFFFFF",
        "LocationId": "RV01",
        "BorderColor": "0x696969",
        "BorderWidthNormalized": 0.1,
        "BoxColor": "0xffffff",
        "BoxWidthNormalized": 0.7,
        "ShadowColor": "0x3c3c3c",
        "ShadowxWidthNormalized": 0.4,
        "ShadowyWidthNormalized": 0.4,
        "SourceLan": "cn",
        "TargetLan": "en",
        "PtsOffset": -1000,
        "SourceLanPerLineWordCount": 28,
        "TargetLanPerLineWordCount": 60,
        "ShowSourceLan": true,
        "ShowTargetLan": true,
        "Truncation": false,
        "AppearDuration": 20000,
        "AppearMode": "Movie"
        }`),
	}

	addCasterComponentResp, err := liveClient.AddCasterComponent(r)
	if err != nil {
		return errors.New("AddCasterComponent gagal: " + err.Error())
	}

	// Terapkan komponen ke adegan yang ditentukan.
	setCasterSceneConfigRequest := &live20161101.UpdateCasterSceneConfigRequest{
		CasterId:    casterId,
		LayoutId:    layoutId,
		SceneId:     pgmSceneId,
		ComponentId: []*string{addCasterComponentResp.Body.ComponentId},
	}

	_, err = liveClient.UpdateCasterSceneConfig(setCasterSceneConfigRequest)
	if err != nil {
		return errors.New("UpdateCasterSceneConfig gagal: " + err.Error())
	}
	return nil
}

// Mulai studio produksi dan atur adegan pemutaran.
func StartCaster(liveClient *live20161101.Client, casterId *string, layoutId *string) (*live20161101.StartCasterResponse, error) {
	// Mulai studio produksi.
	startCasterRequest := &live20161101.StartCasterRequest{
		CasterId: casterId,
	}
	startCasterResp, err := liveClient.StartCaster(startCasterRequest)
	if err != nil {
		return nil, errors.New("StartCaster gagal: " + err.Error())
	}

	// Membutuhkan waktu untuk memuat sumber daya. Jika Anda langsung memulai studio produksi, sumber daya mungkin gagal dimuat. Oleh karena itu, jeda selama satu detik.
	time.Sleep(time.Second)

	// Atur adegan pemutaran.
	// PVW (Opsional)
	setCasterSceneConfigRequest := &live20161101.SetCasterSceneConfigRequest{
		CasterId: casterId,
		LayoutId: layoutId,
		SceneId:  startCasterResp.Body.PvwSceneInfos.SceneInfo[0].SceneId,
	}

	_, err = liveClient.SetCasterSceneConfig(setCasterSceneConfigRequest)
	if err != nil {
		return nil, errors.New("SetCasterSceneConfig gagal: " + err.Error())
	}
	// PGM (Wajib)
	setCasterSceneConfigRequest.SceneId = startCasterResp.Body.PgmSceneInfos.SceneInfo[0].SceneId
	_, err = liveClient.SetCasterSceneConfig(setCasterSceneConfigRequest)
	if err != nil {
		return nil, errors.New("SetCasterSceneConfig gagal: " + err.Error())
	}
	return startCasterResp, nil
}

// Ganti tata letak.
func ChangeLayout(liveClient *live20161101.Client, casterId *string, pgmSceneId *string, newLayoutId *string) error {
	// Ubah ID tata letak adegan.
	setCasterSceneConfigRequest := &live20161101.UpdateCasterSceneConfigRequest{
		CasterId: casterId,
		SceneId:  pgmSceneId,
		LayoutId: newLayoutId,
	}
	_, err := liveClient.UpdateCasterSceneConfig(setCasterSceneConfigRequest)
	if err != nil {
		return errors.New("UpdateCasterSceneConfig gagal: " + err.Error())
	}
	return nil
}

// Hentikan studio produksi.
func StopCaster(liveClient *live20161101.Client, casterId *string) error {
	stopCasterRequest := &live20161101.StopCasterRequest{
		CasterId: casterId,
	}
	_, err := liveClient.StopCaster(stopCasterRequest)
	if err != nil {
		return errors.New("StopCaster gagal: " + err.Error())
	}
	return nil
}

// Hapus studio produksi.
func DeleteCaster(liveClient *live20161101.Client, casterId *string) error {
	deleteCasterRequest := &live20161101.DeleteCasterRequest{
		CasterId: casterId,
	}
	_, err := liveClient.DeleteCaster(deleteCasterRequest)
	if err != nil {
		return errors.New("DeleteCaster gagal: " + err.Error())
	}
	return nil
}

func main() {
	liveClient, err := GetClientInstance()
	if err != nil {
		panic(err)
	}
	// Buat studio produksi.
	createCasterResponse, err := CreateCaster(liveClient)
	if err != nil {
		panic(err)
	}
	casterId := createCasterResponse.Body.CasterId
	log.Println("CasterId: ", *casterId)

	// Konfigurasikan studio produksi.
	domainName := tea.String("example.aliyundoc.com") // Atur nama domain.
	SetCasterConfig(liveClient, casterId, domainName)

	// Tambahkan sumber video. Anda perlu menambahkan sumber video dalam fungsi ini.
	AddCasterVideoResource(liveClient, casterId)

	// Tambahkan tata letak.
	addCasterLayoutResponse, err := AddCasterLayout(liveClient, casterId)
	if err != nil {
		panic(err)
	}
	layoutId := addCasterLayoutResponse.Body.LayoutId

	// Mulai studio produksi dan atur adegan pemutaran.
	startCasterResp, err := StartCaster(liveClient, casterId, layoutId)
	if err != nil {
		panic(err)
	}

	// (Opsional) Tambahkan komponen subtitel real-time dan terapkan ke adegan PGM.
	pgmSceneId := startCasterResp.Body.PgmSceneInfos.SceneInfo[0].SceneId
	AddETComponent(liveClient, casterId, layoutId, pgmSceneId)

	// (Opsional) Ganti tata letak.
	// newLayoutId := tea.String("xxxx")
	// ChangeLayout(liveClient, casterId, pgmSceneId, newLayoutId)

	time.Sleep(time.Minute * 10)
	// Hentikan studio produksi.
	StopCaster(liveClient, casterId)

	// Hapus studio produksi.
	DeleteCaster(liveClient, casterId)
}