Write StampData metadata to video files

This is currently only supported by FFmpeg (so not frameserver, AVI RAW,
or AVI JPEG), and only seems to work when using Matroska or Ogg Theora
containers.

Only metadata that doesn't change from frame to frame is written to
video files. This distinction is visible in the UI by looking at the
stamp checkbox tooltips (they either mention "image" or "image/video").

Part of: https://developer.blender.org/D2273

Reviewed by: @campbellbarton
This commit is contained in:
Sybren A. Stüvel 2018-04-05 16:31:59 +02:00
parent b0a767b85b
commit 6374d390d3
4 changed files with 65 additions and 17 deletions

View File

@ -67,6 +67,11 @@ void BKE_image_init(struct Image *image);
typedef void (StampCallback)(void *data, const char *propname, char *propvalue, int len);
void BKE_render_result_stamp_info(struct Scene *scene, struct Object *camera, struct RenderResult *rr, bool allocate_only);
/**
* Fills in the static stamp data (i.e. everything except things that can change per frame).
* The caller is responsible for freeing the allocated memory.
*/
struct StampData *BKE_stamp_info_from_scene_static(struct Scene *scene);
void BKE_imbuf_stamp_info(struct RenderResult *rr, struct ImBuf *ibuf);
void BKE_stamp_info_from_imbuf(struct RenderResult *rr, struct ImBuf *ibuf);
void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCallback callback, bool noskip);

View File

@ -1623,6 +1623,7 @@ typedef struct StampData {
char marker[512];
char time[512];
char frame[512];
char frame_range[512];
char camera[STAMP_NAME_SIZE];
char cameralens[STAMP_NAME_SIZE];
char scene[STAMP_NAME_SIZE];
@ -1639,7 +1640,12 @@ typedef struct StampData {
} StampData;
#undef STAMP_NAME_SIZE
static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int do_prefix)
/**
* \param do_prefix: Include a label like "File ", "Date ", etc. in the stamp data strings.
* \param use_dynamic: Also include data that can change on a per-frame basis.
*/
static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int do_prefix,
bool use_dynamic)
{
char text[256];
struct tm *tl;
@ -1670,7 +1676,7 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
stamp_data->date[0] = '\0';
}
if (scene->r.stamp & R_STAMP_MARKER) {
if (use_dynamic && scene->r.stamp & R_STAMP_MARKER) {
const char *name = BKE_scene_find_last_marker_name(scene, CFRA);
if (name) BLI_strncpy(text, name, sizeof(text));
@ -1682,7 +1688,7 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
stamp_data->marker[0] = '\0';
}
if (scene->r.stamp & R_STAMP_TIME) {
if (use_dynamic && scene->r.stamp & R_STAMP_TIME) {
const short timecode_style = USER_TIMECODE_SMPTE_FULL;
BLI_timecode_string_from_time(text, sizeof(text), 0, FRA2TIME(scene->r.cfra), FPS, timecode_style);
BLI_snprintf(stamp_data->time, sizeof(stamp_data->time), do_prefix ? "Timecode %s" : "%s", text);
@ -1691,7 +1697,7 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
stamp_data->time[0] = '\0';
}
if (scene->r.stamp & R_STAMP_FRAME) {
if (use_dynamic && scene->r.stamp & R_STAMP_FRAME) {
char fmtstr[32];
int digits = 1;
@ -1705,14 +1711,14 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
stamp_data->frame[0] = '\0';
}
if (scene->r.stamp & R_STAMP_CAMERA) {
if (use_dynamic && scene->r.stamp & R_STAMP_CAMERA) {
BLI_snprintf(stamp_data->camera, sizeof(stamp_data->camera), do_prefix ? "Camera %s" : "%s", camera ? camera->id.name + 2 : "<none>");
}
else {
stamp_data->camera[0] = '\0';
}
if (scene->r.stamp & R_STAMP_CAMERALENS) {
if (use_dynamic && scene->r.stamp & R_STAMP_CAMERALENS) {
if (camera && camera->type == OB_CAMERA) {
BLI_snprintf(text, sizeof(text), "%.2f", ((Camera *)camera->data)->lens);
}
@ -1733,7 +1739,7 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
stamp_data->scene[0] = '\0';
}
if (scene->r.stamp & R_STAMP_SEQSTRIP) {
if (use_dynamic && scene->r.stamp & R_STAMP_SEQSTRIP) {
Sequence *seq = BKE_sequencer_foreground_frame_get(scene, scene->r.cfra);
if (seq) BLI_strncpy(text, seq->name + 2, sizeof(text));
@ -1749,7 +1755,7 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
Render *re = RE_GetSceneRender(scene);
RenderStats *stats = re ? RE_GetStats(re) : NULL;
if (stats && (scene->r.stamp & R_STAMP_RENDERTIME)) {
if (use_dynamic && stats && (scene->r.stamp & R_STAMP_RENDERTIME)) {
BLI_timecode_string_from_time_simple(text, sizeof(text), stats->lastframetime);
BLI_snprintf(stamp_data->rendertime, sizeof(stamp_data->rendertime), do_prefix ? "RenderTime %s" : "%s", text);
@ -1758,7 +1764,7 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d
stamp_data->rendertime[0] = '\0';
}
if (stats && (scene->r.stamp & R_STAMP_MEMORY)) {
if (use_dynamic && stats && (scene->r.stamp & R_STAMP_MEMORY)) {
BLI_snprintf(stamp_data->memory, sizeof(stamp_data->memory), do_prefix ? "Peak Memory %.2fM" : "%.2fM", stats->mem_peak);
}
else {
@ -1885,7 +1891,7 @@ void BKE_image_stamp_buf(
display = IMB_colormanagement_display_get_named(display_device);
if (stamp_data_template == NULL) {
stampdata(scene, camera, &stamp_data, (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0);
stampdata(scene, camera, &stamp_data, (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0, true);
}
else {
stampdata_from_template(&stamp_data, scene, stamp_data_template);
@ -2106,13 +2112,28 @@ void BKE_render_result_stamp_info(Scene *scene, Object *camera, struct RenderRes
}
if (!allocate_only)
stampdata(scene, camera, stamp_data, 0);
stampdata(scene, camera, stamp_data, 0, true);
if (!rr->stamp_data) {
rr->stamp_data = stamp_data;
}
}
struct StampData *BKE_stamp_info_from_scene_static(Scene *scene)
{
struct StampData *stamp_data;
if (!(scene && (scene->r.stamp & R_STAMP_ALL)))
return NULL;
/* Memory is allocated here (instead of by the caller) so that the caller
* doesn't have to know the size of the StampData struct. */
stamp_data = MEM_callocN(sizeof(StampData), __func__);
stampdata(scene, NULL, stamp_data, 0, false);
return stamp_data;
}
void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCallback callback, bool noskip)
{
if ((callback == NULL) || (stamp_data == NULL)) {

View File

@ -53,6 +53,7 @@
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_sound.h"
@ -62,6 +63,8 @@
#include "ffmpeg_compat.h"
struct StampData;
typedef struct FFMpegContext {
int ffmpeg_type;
int ffmpeg_codec;
@ -94,6 +97,8 @@ typedef struct FFMpegContext {
bool audio_deinterleave;
int audio_sample_size;
struct StampData *stamp_data;
#ifdef WITH_AUDASPACE
AUD_Device *audio_mixdown_device;
#endif
@ -836,6 +841,12 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va
av_dict_set(dict, key, buffer, 0);
}
static void ffmpeg_add_metadata_callback(void *data, const char *propname, char *propvalue, int len)
{
AVDictionary **metadata = (AVDictionary **)data;
av_dict_set(metadata, propname, propvalue, 0);
}
static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int rectx, int recty, const char *suffix, ReportList *reports)
{
/* Handle to the output file */
@ -994,6 +1005,11 @@ static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int
goto fail;
}
}
if (context->stamp_data != NULL) {
BKE_stamp_info_callback(&of->metadata, context->stamp_data, ffmpeg_add_metadata_callback, false);
}
if (avformat_write_header(of, NULL) < 0) {
BKE_report(reports, RPT_ERROR, "Could not initialize streams, probably unsupported codec combination");
goto fail;
@ -1168,6 +1184,7 @@ int BKE_ffmpeg_start(void *context_v, struct Scene *scene, RenderData *rd, int r
context->ffmpeg_autosplit_count = 0;
context->ffmpeg_preview = preview;
context->stamp_data = BKE_stamp_info_from_scene_static(scene);
success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
#ifdef WITH_AUDASPACE
@ -1734,6 +1751,7 @@ void *BKE_ffmpeg_context_create(void)
context->ffmpeg_autosplit = 0;
context->ffmpeg_autosplit_count = 0;
context->ffmpeg_preview = false;
context->stamp_data = NULL;
return context;
}
@ -1741,9 +1759,13 @@ void *BKE_ffmpeg_context_create(void)
void BKE_ffmpeg_context_free(void *context_v)
{
FFMpegContext *context = context_v;
if (context) {
MEM_freeN(context);
if (context == NULL) {
return;
}
if (context->stamp_data) {
MEM_freeN(context->stamp_data);
}
MEM_freeN(context);
}
#endif /* WITH_FFMPEG */

View File

@ -6355,7 +6355,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_stamp_date", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_DATE);
RNA_def_property_ui_text(prop, "Stamp Date", "Include the current date in image metadata");
RNA_def_property_ui_text(prop, "Stamp Date", "Include the current date in image/video metadata");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "use_stamp_frame", PROP_BOOLEAN, PROP_NONE);
@ -6375,12 +6375,12 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_stamp_scene", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_SCENE);
RNA_def_property_ui_text(prop, "Stamp Scene", "Include the name of the active scene in image metadata");
RNA_def_property_ui_text(prop, "Stamp Scene", "Include the name of the active scene in image/video metadata");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "use_stamp_note", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_NOTE);
RNA_def_property_ui_text(prop, "Stamp Note", "Include a custom note in image metadata");
RNA_def_property_ui_text(prop, "Stamp Note", "Include a custom note in image/video metadata");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "use_stamp_marker", PROP_BOOLEAN, PROP_NONE);
@ -6390,7 +6390,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_stamp_filename", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_FILENAME);
RNA_def_property_ui_text(prop, "Stamp Filename", "Include the .blend filename in image metadata");
RNA_def_property_ui_text(prop, "Stamp Filename", "Include the .blend filename in image/video metadata");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "use_stamp_sequencer_strip", PROP_BOOLEAN, PROP_NONE);