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

Experiment deinterlace #58

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions avpipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ type XcParams struct {
Rotate int `json:"rotate"`
Profile string `json:"profile"`
Level int `json:"level"`
Deinterlace int `json:"deinterlace"`
}

// NewXcParams initializes a XcParams struct with unset/default values
Expand Down Expand Up @@ -1306,6 +1307,7 @@ func getCParams(params *XcParams) (*C.xcparams_t, error) {
rotate: C.int(params.Rotate),
profile: C.CString(params.Profile),
level: C.int(params.Level),
deinterlace: C.dif_type(params.Deinterlace),

// All boolean params are handled below
}
Expand Down
31 changes: 31 additions & 0 deletions doc/dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# DEVELOPER NOTES



## HOW TO

### Transcode a "content part" into ABR segments

```
./bin/exc -f hqp_3VQ52.mp4 -format dash -xc-type video -video-seg-duration-ts 120120
```

- output files: `./O/O1/`


### Transcode a "content part" into encrypted ABR segments


```
./bin/exc -f hqp_3VQ52.mp4 -format dash -xc-type video -video-seg-duration-ts 120120 -crypt-scheme cenc -crypt-key 13c396f04c947a0b0c9794f7f114a614 -crypt-kid 351a81dbaf704ab43ce7f3b4fbc603b3 -crypt-iv 9c33afa0bff3c27f671fa8e91f31f2c9
```

## Special Use Cases

### Deinterlacing

```
./bin/exc -f ./test.mp4 -format fmp4-segment -seg-duration 30 -video-time-base 100 -video-frame-duration-ts 256 -force-keyint 100

./elvxc/elvxc transcode -f ./test.mxf --video-time-base 100 --video-frame-duration-ts 256 --xc-type video --deinterlace 1 --force-keyint 100 --format fmp4-segment
```
7 changes: 7 additions & 0 deletions elvxc/cmd/transcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ func InitTranscode(cmdRoot *cobra.Command) error {
cmdTranscode.PersistentFlags().Int32("rotate", 0, "Rotate the output video frame (valid values 0, 90, 180, 270).")
cmdTranscode.PersistentFlags().StringP("profile", "", "", "Encoding profile for video. If it is not determined, it will be set automatically.")
cmdTranscode.PersistentFlags().Int32("level", 0, "Encoding level for video. If it is not determined, it will be set automatically.")
cmdTranscode.PersistentFlags().Int32("deinterlace", 0, "Deinterlace filter (values 0 - none, 1 - bwdif_field, 2 - bwdif_frame send_frame).")

return nil
}
Expand Down Expand Up @@ -611,6 +612,11 @@ func doTranscode(cmd *cobra.Command, args []string) error {

profile := cmd.Flag("profile").Value.String()

deinterlace, err := cmd.Flags().GetInt32("deinterlace")
if err != nil {
return fmt.Errorf("Invalid deinterlace value")
}

cryptScheme := avpipe.CryptNone
val := cmd.Flag("crypt-scheme").Value.String()
if len(val) > 0 {
Expand Down Expand Up @@ -709,6 +715,7 @@ func doTranscode(cmd *cobra.Command, args []string) error {
Rotate: int(rotate),
Profile: profile,
Level: int(level),
Deinterlace: int(deinterlace),
}

err = getAudioIndexes(params, audioIndex)
Expand Down
13 changes: 12 additions & 1 deletion exc/elv_xc.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ in_stat(
if (debug_frame_level)
elv_dbg("IN STAT stream_index=%d, fd=%d, video frame read=%"PRId64, stream_index, fd, c->video_frames_read);
break;
case in_stat_first_keyframe_pts:
if (debug_frame_level)
elv_dbg("IN STAT fd=%d, first keyframe PTS=%"PRId64", url=%s", fd, c->first_key_frame_pts, c->url);
break;
case in_stat_data_scte35:
if (debug_frame_level)
elv_dbg("IN STAT stream_index=%d, fd=%d, data=%s", stream_index, fd, c->data);
Expand Down Expand Up @@ -1028,6 +1032,7 @@ usage(
"\t-d : (optional) Decoder name. For video default is \"h264\", can be: \"h264\", \"h264_cuvid\", \"jpeg2000\", \"hevc\"\n"
"\t For audio default is \"aac\", but for ts files should be set to \"ac3\"\n"
"\t-debug-frame-level : (optional) Enable/disable debug frame level. Default is 0, must be 0 or 1.\n"
"\t-deinterlace : (optional) Deinterlace filter. Default is 0 (none), can be: 1 (bwdif send_field), 2 (bwdif send_frame)\n"
"\t-duration-ts : (optional) Default: -1 (entire stream)\n"
"\t-e : (optional) Video encoder name. Default is \"libx264\", can be: \"libx264\", \"libx265\", \"h264_nvenc\", \"hevc_nvenc\", \"h264_videotoolbox\", or \"mjpeg\"\n"
"\t-enc-height : (optional) Default: -1 (use source height)\n"
Expand Down Expand Up @@ -1167,6 +1172,7 @@ main(
.start_fragment_index = 0, /* Default is zero */
.sync_audio_to_stream_id = -1, /* Default -1 (no sync to a video stream) */
.rotate = 0, /* Default 0 (means no transpose/rotation) */
.deinterlace = 0, /* Default 0 (no deinterlacing) */
.xc_type = xc_none,
.video_bitrate = -1, /* not used if using CRF */
.watermark_text = NULL,
Expand Down Expand Up @@ -1284,7 +1290,12 @@ main(
if (p.debug_frame_level != 0 && p.debug_frame_level != 1) {
usage(argv[0], argv[i], EXIT_FAILURE);
}
} else if (strlen(argv[i]) > 2) {
} else if (!strcmp(argv[i], "-deinterlace")) {
if (sscanf(argv[i+1], "%d", &p.deinterlace) != 1) {
usage(argv[0], argv[i], EXIT_FAILURE);
}
}
else if (strlen(argv[i]) > 2) {
usage(argv[0], argv[i], EXIT_FAILURE);
} else {
p.dcodec = strdup(argv[i+1]);
Expand Down
8 changes: 8 additions & 0 deletions libavpipe/include/avpipe_xc.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,13 @@ typedef enum image_type {
gif_image
} image_type;

// deinterlacing filter types
typedef enum dif_type {
dif_none = 0, // No deinterlacing
dif_bwdif = 1, // Use filter bwdif mode 'send_field' (two frames per input frame)
dif_bwdif_frame = 2 // Use filter bwdif mode 'send_frame' (one frame per input frame)
} dif_type;

#define DRAW_TEXT_SHADOW_OFFSET 0.075
#define MAX_EXTRACT_IMAGES_SZ 100

Expand Down Expand Up @@ -452,6 +459,7 @@ typedef struct xcparams_t {
int rotate; // For video transpose or rotation
char *profile;
int level;
dif_type deinterlace; // Deinterlacing filter
} xcparams_t;

#define MAX_CODEC_NAME 256
Expand Down
44 changes: 42 additions & 2 deletions libavpipe/src/avpipe_xc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,7 @@ prepare_video_encoder(
out_stream->time_base = (AVRational) {1, params->video_time_base};
else
out_stream->time_base = in_stream->time_base;

out_stream->avg_frame_rate = decoder_context->format_context->streams[decoder_context->video_stream_index]->avg_frame_rate;
out_stream->codecpar->codec_tag = 0;

Expand Down Expand Up @@ -1237,13 +1238,15 @@ prepare_video_encoder(
encoder_codec_context->time_base = (AVRational) {1, params->video_time_base};
else
encoder_codec_context->time_base = decoder_context->codec_context[index]->time_base;

encoder_codec_context->sample_aspect_ratio = decoder_context->codec_context[index]->sample_aspect_ratio;
if (params->video_bitrate > 0)
encoder_codec_context->bit_rate = params->video_bitrate;
if (params->rc_buffer_size > 0)
encoder_codec_context->rc_buffer_size = params->rc_buffer_size;
if (params->rc_max_rate > 0)
encoder_codec_context->rc_max_rate = params->rc_max_rate;

encoder_codec_context->framerate = decoder_context->codec_context[index]->framerate;

// This needs to be set before open (ffmpeg samples have it wrong)
Expand Down Expand Up @@ -3417,6 +3420,42 @@ get_filter_str(
{
*filter_str = NULL;

// Validate filter compatibility
// Note these filters can theoretically be made to work together but not a real use case
if (params->rotate > 0 || params->deinterlace != dif_none) {
if ((params->watermark_text && *params->watermark_text != '\0') ||
(params->watermark_overlay && params->watermark_overlay[0] != '\0')) {
elv_err("Incompatible filter parameters - watermark not supported with rotate and deinterlacing");
return eav_param;
}
if (params->rotate > 0 && params->deinterlace != dif_none) {
elv_err("Incompatible filter parameters - both rotate and deinterlacing");
return eav_param;
}
if (params->deinterlace == dif_bwdif) {
// This filter needs to create two output frames for each input frame and
// requires the caller to specify the new frame duration (1/2 of input frame duration)
if (params->video_frame_duration_ts == 0) {
elv_err("Incorrect filter spec - deinterlacing requires frame duration");
return eav_param;
}
}
}

// General syntax for the bwdif filter:
// "bwdif=mode=send_frame:parity=auto:deint=all"
switch (params->deinterlace) {
case dif_bwdif:
*filter_str = strdup("bwdif=mode=send_field");
return eav_success;
case dif_bwdif_frame:
*filter_str = strdup("bwdif=mode=send_frame");
return eav_success;
default:
// Nothing to do
break;
}

if (params->rotate > 0) {
switch (params->rotate) {
case 90:
Expand Down Expand Up @@ -4634,7 +4673,8 @@ log_params(
"video_frame_duration_ts=%d "
"rotate=%d "
"profile=%s "
"level=%d",
"level=%d "
"deinterlace=%d",
params->stream_id, params->url,
avpipe_version(),
params->bypass_transcoding, params->skip_decoding,
Expand All @@ -4658,7 +4698,7 @@ log_params(
params->filter_descriptor,
params->extract_image_interval_ts, params->extract_images_sz,
1, params->video_time_base, params->video_frame_duration_ts, params->rotate,
params->profile ? params->profile : "", params->level);
params->profile ? params->profile : "", params->level, params->deinterlace);
elv_log("AVPIPE XCPARAMS %s", buf);
}

Expand Down