Reduce audio and video bitrates
This commit is contained in:
parent
93736abc24
commit
7d91340582
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue