Reduce audio and video bitrates

This commit is contained in:
Rob Watson 2021-10-19 17:37:54 +02:00
parent 93736abc24
commit 7d91340582
2 changed files with 12 additions and 17 deletions

View File

@ -33,10 +33,9 @@ func FormatDebugString(format *youtubev2.Format, includeURL bool) string {
} }
// SortAudio returns the provided formats ordered in descending preferred // SortAudio returns the provided formats ordered in descending preferred
// order. The ideal candidate is 44.1kHz stereo audio in a webm container, with // order. The ideal candidate is opus-encoded stereo audio in a webm container,
// the highest available bitrate. // with the lowest available bitrate.
func SortAudio(inFormats youtubev2.FormatList) youtubev2.FormatList { func SortAudio(inFormats youtubev2.FormatList) youtubev2.FormatList {
// TODO: sort in-place.
var formats youtubev2.FormatList var formats youtubev2.FormatList
for _, format := range inFormats { for _, format := range inFormats {
if format.FPS == 0 && format.AudioChannels > 0 { if format.FPS == 0 && format.AudioChannels > 0 {
@ -50,12 +49,7 @@ func SortAudio(inFormats youtubev2.FormatList) youtubev2.FormatList {
isStereoI := formats[i].AudioChannels == 2 isStereoI := formats[i].AudioChannels == 2
isStereoJ := formats[j].AudioChannels == 2 isStereoJ := formats[j].AudioChannels == 2
if isStereoI && isStereoJ { if isStereoI && isStereoJ {
is48kI := formats[i].AudioSampleRate == "48000" return formats[i].ContentLength < formats[j].ContentLength
is48kJ := formats[j].AudioSampleRate == "48000"
if is48kI && is48kJ {
return formats[i].Bitrate > formats[j].Bitrate
}
return is48kI
} }
return isStereoI return isStereoI
} }
@ -69,10 +63,9 @@ func SortAudio(inFormats youtubev2.FormatList) youtubev2.FormatList {
// bitrate, with audio channels (needed to allow synced playback on the // bitrate, with audio channels (needed to allow synced playback on the
// website). // website).
func SortVideo(inFormats youtubev2.FormatList) youtubev2.FormatList { func SortVideo(inFormats youtubev2.FormatList) youtubev2.FormatList {
// TODO: sort in-place.
var formats youtubev2.FormatList var formats youtubev2.FormatList
for _, format := range inFormats { for _, format := range inFormats {
if format.FPS > 0 && format.ContentLength > 0 && format.AudioChannels > 0 { if format.FPS > 0 && format.ContentLength > 0 {
formats = append(formats, format) formats = append(formats, format)
} }
} }
@ -80,7 +73,7 @@ func SortVideo(inFormats youtubev2.FormatList) youtubev2.FormatList {
isMP4I := strings.Contains(formats[i].MimeType, "mp4") isMP4I := strings.Contains(formats[i].MimeType, "mp4")
isMP4J := strings.Contains(formats[j].MimeType, "mp4") isMP4J := strings.Contains(formats[j].MimeType, "mp4")
if isMP4I && isMP4J { if isMP4I && isMP4J {
return formats[i].Bitrate < formats[j].Bitrate return formats[i].ContentLength < formats[j].ContentLength
} }
return isMP4I return isMP4I
}) })

View File

@ -88,7 +88,7 @@ func (d *Downloader) Download(ctx context.Context, videoID string) (*media.Media
}() }()
go func() { go func() {
defer close(videoResultChan) defer close(videoResultChan)
video, videoErr := d.downloadVideo(ctx, video, mediaSet.VideoPath(), mediaSet.ThumbnailPath()) video, videoErr := d.downloadVideo(ctx, video, mediaSet.VideoPath())
result := videoResult{video, videoErr} result := videoResult{video, videoErr}
videoResultChan <- result videoResultChan <- result
wg.Done() wg.Done()
@ -138,6 +138,7 @@ func (d *Downloader) downloadAudio(ctx context.Context, video *youtubev2.Video,
if err != nil { if err != nil {
return nil, fmt.Errorf("error fetching audio stream: %v", err) return nil, fmt.Errorf("error fetching audio stream: %v", err)
} }
reader := progressReader{Reader: stream, label: "audio", exp: int(format.ContentLength)}
rawAudioFile, err := os.Create(rawOutPath) rawAudioFile, err := os.Create(rawOutPath)
if err != nil { if err != nil {
@ -148,7 +149,7 @@ func (d *Downloader) downloadAudio(ctx context.Context, video *youtubev2.Video,
if err != nil { if err != nil {
return nil, fmt.Errorf("error creating encoded audio file: %v", err) return nil, fmt.Errorf("error creating encoded audio file: %v", err)
} }
streamReader := io.TeeReader(stream, encodedAudioFile) streamReader := io.TeeReader(&reader, encodedAudioFile)
var errOut bytes.Buffer var errOut bytes.Buffer
cmd := exec.CommandContext(ctx, "ffmpeg", "-i", "-", "-f", rawAudioFormat, "-ar", strconv.Itoa(rawAudioSampleRate), "-acodec", rawAudioCodec, "-") cmd := exec.CommandContext(ctx, "ffmpeg", "-i", "-", "-f", rawAudioFormat, "-ar", strconv.Itoa(rawAudioSampleRate), "-acodec", rawAudioCodec, "-")
@ -191,6 +192,7 @@ func (d *Downloader) downloadAudio(ctx context.Context, video *youtubev2.Video,
type progressReader struct { type progressReader struct {
io.Reader io.Reader
label string
total, exp int total, exp int
} }
@ -198,12 +200,12 @@ func (pw *progressReader) Read(p []byte) (int, error) {
n, err := pw.Reader.Read(p) n, err := pw.Reader.Read(p)
pw.total += n pw.total += n
log.Printf("[ProgressReader] Read %d of %d (%.02f%%) bytes from the provided reader", pw.total, pw.exp, (float32(pw.total)/float32(pw.exp))*100.0) log.Printf("[ProgressReader] [%s] Read %d of %d (%.02f%%) bytes from the provided reader", pw.label, pw.total, pw.exp, (float32(pw.total)/float32(pw.exp))*100.0)
return n, err return n, err
} }
func (d *Downloader) downloadVideo(ctx context.Context, video *youtubev2.Video, outPath string, thumbnailOutPath string) (*media.Video, error) { func (d *Downloader) downloadVideo(ctx context.Context, video *youtubev2.Video, outPath string) (*media.Video, error) {
if len(video.Formats) == 0 { if len(video.Formats) == 0 {
return nil, errors.New("error selecting audio format: no format available") return nil, errors.New("error selecting audio format: no format available")
} }
@ -219,7 +221,7 @@ func (d *Downloader) downloadVideo(ctx context.Context, video *youtubev2.Video,
if err != nil { if err != nil {
return nil, fmt.Errorf("error fetching video stream: %v", err) return nil, fmt.Errorf("error fetching video stream: %v", err)
} }
reader := progressReader{Reader: stream, exp: int(format.ContentLength)} reader := progressReader{Reader: stream, label: "video", exp: int(format.ContentLength)}
videoFile, err := os.Create(outPath) videoFile, err := os.Create(outPath)
if err != nil { if err != nil {