Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ffmpeg: Add demuxer options. #426

Merged
merged 1 commit into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions ffmpeg/decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,19 @@ int open_input(input_params *params, struct input_ctx *ctx)

ctx->transmuxing = params->transmuxing;

// open demuxer/ open demuxer
const AVInputFormat *fmt = NULL;
if (params->demuxer.name) {
fmt = av_find_input_format(params->demuxer.name);
if (!fmt) {
ret = AVERROR_DEMUXER_NOT_FOUND;
LPMS_ERR(open_input_err, "Invalid demuxer name")
}
}

// open demuxer
AVDictionary **demuxer_opts = NULL;
if (params->demuxer.opts) demuxer_opts = &params->demuxer.opts;
ret = avformat_open_input(&ic, inp, NULL, demuxer_opts);
ret = avformat_open_input(&ic, inp, fmt, demuxer_opts);
if (ret < 0) LPMS_ERR(open_input_err, "demuxer: Unable to open input");
// If avformat_open_input replaced the options AVDictionary with options that were not found free it
if (demuxer_opts) av_dict_free(demuxer_opts);
Expand Down
20 changes: 15 additions & 5 deletions ffmpeg/ffmpeg.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ type TranscodeOptionsIn struct {
Device string
Transmuxing bool
Profile VideoProfile
Demuxer ComponentOptions
}

type TranscodeOptions struct {
Expand Down Expand Up @@ -948,6 +949,12 @@ func (t *Transcoder) Transcode(input *TranscodeOptionsIn, ps []TranscodeOptions)

var demuxerOpts C.component_opts

if input.Demuxer.Name != "" {
demuxerName := C.CString(input.Demuxer.Name)
defer C.free(unsafe.Pointer(demuxerName))
demuxerOpts.name = demuxerName
}

ext := filepath.Ext(input.Fname)
// If the input has an image file extension setup the image2 demuxer
if ext == ".png" {
Expand All @@ -963,14 +970,17 @@ func (t *Transcoder) Transcode(input *TranscodeOptionsIn, ps []TranscodeOptions)
input.Profile.FramerateDen = 1
}

// Do not try to free in this function because in the C code avformat_open_input()
// will destroy this
demuxerOpts.opts = newAVOpts(map[string]string{
"framerate": fmt.Sprintf("%d/%d", input.Profile.Framerate, input.Profile.FramerateDen),
})
// changing the input map here is maybe not great
input.Demuxer.Opts["framerate"] = fmt.Sprintf("%d/%d", input.Profile.Framerate, input.Profile.FramerateDen)
}
}

if len(input.Demuxer.Opts) > 0 {
// Do not free in this function because avformat_open_input()
// in the C code will destroy this
demuxerOpts.opts = newAVOpts(input.Demuxer.Opts)
}

inp := &C.input_params{fname: fname, hw_type: hw_type, device: device, xcoderParams: xcoderParams,
handle: t.handle, demuxer: demuxerOpts}
if input.Transmuxing {
Expand Down
62 changes: 62 additions & 0 deletions ffmpeg/ffmpeg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2377,3 +2377,65 @@ func runRotationTests(t *testing.T, accel Acceleration) {
`
run(cmd)
}

func TestTranscoder_DemuxerOpts(t *testing.T) {
// generate test files: a few frames of raw video
run, dir := setupTest(t)
defer os.RemoveAll(dir)

cmd := `
# use an unusual pixel format
ffmpeg -i "$1/../transcoder/test.ts" -an -c:v rawvideo -pix_fmt gbrp12be -s 320x240 -r 1 -frames:v 3 -f rawvideo test.raw
ffprobe -show_streams -count_frames -pixel_format gbrp12be -video_size 320x240 -f rawvideo test.raw 2>&1 | grep nb_read_frames=3
`
run(cmd)
res, err := Transcode3(
&TranscodeOptionsIn{
Fname: dir + "/test.raw",
Demuxer: ComponentOptions{
Name: "rawvideo",
Opts: map[string]string{
"fflags": "+discardcorrupt+nobuffer",
"pixel_format": "gbrp12be",
"video_size": "320x240",
},
},
},
[]TranscodeOptions{{
Oname: dir + "/out-%d.png",
Profile: VideoProfile{
Name: "-",
Resolution: "200x150",
Bitrate: "10k",
},
}})
assert.Nil(t, err, "transcoder returned error")
assert := assert.New(t)
// we transcode 3 but decode/encode 2 due to nobuffer
assert.Equal(2, res.Decoded.Frames, "decoded frame count did not match")
assert.Equal(2, res.Encoded[0].Frames, "encoded frame count did not match")
assert.Equal(int64(2*320*240), res.Decoded.Pixels, "decoded pixel count did not match")
assert.Equal(int64(2*200*150), res.Encoded[0].Pixels, "encoded frame count did not match")
}

func TestTranscoder_DemuxerOptsError(t *testing.T) {

// nonexistent demuxer
_, err := Transcode3(&TranscodeOptionsIn{
Fname: "../transcoder/test.ts",
Demuxer: ComponentOptions{
Name: "nonexistent",
},
}, nil)
assert.Equal(t, "Demuxer not found", err.Error())

// wrong demuxer
_, err = Transcode3(&TranscodeOptionsIn{
Fname: "../transcoder/test.ts",
Demuxer: ComponentOptions{
Name: "mp4",
},
}, nil)
assert.Equal(t, "Invalid data found when processing input", err.Error())

}
10 changes: 9 additions & 1 deletion ffmpeg/transcoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,15 @@ int transcode_init(struct transcode_thread *h, input_params *inp,
if (!ictx->ic) {
// reopen demuxer for the input segment if needed
// XXX could open_input() be re-used here?
ret = avformat_open_input(&ictx->ic, inp->fname, NULL, demuxer_opts);
const AVInputFormat *fmt = NULL;
if (inp->demuxer.name) {
fmt = av_find_input_format(inp->demuxer.name);
if (!fmt) {
ret = AVERROR_DEMUXER_NOT_FOUND;
LPMS_ERR(transcode_cleanup, "Invalid demuxer name")
}
}
ret = avformat_open_input(&ictx->ic, inp->fname, fmt, demuxer_opts);
if (ret < 0) LPMS_ERR(transcode_cleanup, "Unable to reopen demuxer");
// If avformat_open_input replaced the options AVDictionary with options that were not found free it
if (demuxer_opts) av_dict_free(demuxer_opts);
Expand Down
Loading