summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRodeo <[email protected]>2014-02-28 00:29:48 +0000
committerRodeo <[email protected]>2014-02-28 00:29:48 +0000
commit733bdedc5f71442c535a8bcc1c239d77c7b15036 (patch)
treef42dd7646e229d589a5abf033e165252133030a7
parent525c8a11b4930ff40a94897fc65ebea2fac7126f (diff)
Bump x265 to r6321 8a84c10e5116.
- rational framerates - libx265 now provides decode time stamps (DTS) - encx265: minor refactoring + prepare for muxing to MP4/Matroska git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6088 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--contrib/x265/module.defs2
-rw-r--r--libhb/encx265.c294
-rw-r--r--libhb/internal.h6
-rw-r--r--libhb/muxavformat.c16
4 files changed, 236 insertions, 82 deletions
diff --git a/contrib/x265/module.defs b/contrib/x265/module.defs
index 230ac15e6..ca41327b3 100644
--- a/contrib/x265/module.defs
+++ b/contrib/x265/module.defs
@@ -1,7 +1,7 @@
$(eval $(call import.MODULE.defs,X265,x265,YASM))
$(eval $(call import.CONTRIB.defs,X265))
-X265.FETCH.url = http://download.handbrake.fr/contrib/x265-5825-9e923f539d89.tar.bz2
+X265.FETCH.url = http://download.handbrake.fr/contrib/x265-6321-8a84c10e5116.tar.bz2
X265.CONFIGURE.exe = cmake
X265.CONFIGURE.args.prefix = -DCMAKE_INSTALL_PREFIX="$(X265.CONFIGURE.prefix)"
diff --git a/libhb/encx265.c b/libhb/encx265.c
index 5ffc547c3..610d06128 100644
--- a/libhb/encx265.c
+++ b/libhb/encx265.c
@@ -15,7 +15,6 @@
int encx265Init (hb_work_object_t*, hb_job_t*);
int encx265Work (hb_work_object_t*, hb_buffer_t**, hb_buffer_t**);
void encx265Close(hb_work_object_t*);
-void writeNALs (hb_work_private_t*, const x265_nal*, int);
hb_work_object_t hb_encx265 =
{
@@ -42,13 +41,9 @@ struct hb_work_private_s
hb_job_t *job;
x265_encoder *x265;
x265_param *param;
- x265_picture pic_in; // TODO: use x265_picture_alloc
- x265_nal *p_nal;
- uint32_t nal_count;
int64_t last_stop;
uint32_t frames_in;
- uint32_t frames_out;
hb_list_t *delayed_chapters;
int64_t next_chapter_pts;
@@ -83,9 +78,21 @@ int encx265Init(hb_work_object_t *w, hb_job_t *job)
pv->delayed_chapters = hb_list_init();
pv->job = job;
w->private_data = pv;
+ int i, vrate, vrate_base;
+ x265_nal *nal;
+ uint32_t nnal;
- pv->fout = fopen(job->file, "wb");
- fseek(pv->fout, 0, SEEK_SET);
+ if (job->mux == HB_MUX_X265)
+ {
+ pv->fout = fopen(job->file, "wb");
+ if (pv->fout == NULL || fseek(pv->fout, 0L, SEEK_SET) < 0)
+ {
+ hb_error("encx265: fopen failed.");
+ free(pv);
+ pv = NULL;
+ return 1;
+ }
+ }
x265_param *param = pv->param = x265_param_alloc();
@@ -124,9 +131,11 @@ int encx265Init(hb_work_object_t *w, hb_job_t *job)
* Some HandBrake-specific defaults; users can override them
* using the encoder_options string.
*/
- param->frameRate = (int)((double)job->vrate / (double)job->vrate_base + 0.5); // yes, this is an int
- param->keyframeMax = param->frameRate * 10;
- param->keyframeMin = param->frameRate;
+ hb_reduce(&vrate, &vrate_base, job->vrate, job->vrate_base);
+ param->fpsNum = vrate;
+ param->fpsDenom = vrate_base;
+ param->keyframeMin = (int)((double)vrate / (double)vrate_base + 0.5);
+ param->keyframeMax = param->keyframeMin * 10;
/* iterate through x265_opts and parse the options */
hb_dict_entry_t *entry = NULL;
@@ -204,11 +213,47 @@ int encx265Init(hb_work_object_t *w, hb_job_t *job)
pv = NULL;
return 1;
}
- if (!x265_encoder_headers(pv->x265, &pv->p_nal, &pv->nal_count))
+
+ if (x265_encoder_headers(pv->x265, &nal, &nnal) < 0)
+ {
+ hb_error("encx265: x265_encoder_headers failed.");
+ free(pv);
+ pv = NULL;
+ return 1;
+ }
+
+ if (job->mux == HB_MUX_X265)
+ {
+ for (i = 0; i < nnal; i++)
+ {
+ fwrite(nal[i].payload, 1, nal[i].sizeBytes, pv->fout);
+ }
+ return 0;
+ }
+
+ /*
+ * x265's output (headers and bitstream) are in Annex B format.
+ *
+ * Write the header as is, and let the muxer reformat
+ * the extradata and output bitstream properly for us.
+ */
+ w->config->h265.headers_length = 0;
+ for (i = 0; i < nnal; i++)
{
- writeNALs(pv, pv->p_nal, pv->nal_count);
+ if (w->config->h265.headers_length +
+ nal[i].sizeBytes > HB_CONFIG_MAX_SIZE)
+ {
+ hb_error("encx265: bitstream headers too large");
+ free(pv);
+ pv = NULL;
+ return 1;
+ }
+ memcpy(w->config->h265.headers +
+ w->config->h265.headers_length,
+ nal[i].payload, nal[i].sizeBytes);
+ w->config->h265.headers_length += nal[i].sizeBytes;
}
- x265_picture_init(param, &pv->pic_in);
+
return 0;
}
@@ -243,42 +288,138 @@ static void save_frame_info(hb_work_private_t *pv, hb_buffer_t *in)
int i = (in->s.start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK;
pv->frame_info[i].duration = in->s.stop - in->s.start;
}
+static int64_t get_frame_duration(hb_work_private_t * pv, int64_t pts)
+{
+ int i = (pts >> FRAME_INFO_MAX2) & FRAME_INFO_MASK;
+ return pv->frame_info[i].duration;
+}
-void writeNALs(hb_work_private_t * pv, const x265_nal* nal, int nalcount)
+static hb_buffer_t* nal_encode(hb_work_object_t *w,
+ x265_picture *pic_out,
+ x265_nal *nal, uint32_t nnal)
{
+ hb_work_private_t *pv = w->private_data;
+ hb_job_t *job = pv->job;
+ hb_buffer_t *buf = NULL;
int i;
- for (i = 0; i < nalcount; i++)
+
+ if (nnal <= 0)
+ {
+ return NULL;
+ }
+
+ buf = hb_video_buffer_init(job->width, job->height);
+ if (buf == NULL)
+ {
+ return NULL;
+ }
+
+ buf->size = 0;
+ // copy the bitstream data
+ for (i = 0; i < nnal; i++)
+ {
+ memcpy(buf->data + buf->size, nal[i].payload, nal[i].sizeBytes);
+ buf->size += nal[i].sizeBytes;
+ }
+
+ // use the pts to get the original frame's duration.
+ buf->s.duration = get_frame_duration(pv, pic_out->pts);
+ buf->s.stop = pic_out->pts + buf->s.duration;
+ buf->s.start = pic_out->pts;
+ buf->s.renderOffset = pic_out->dts;
+ if (w->config->h264.init_delay == 0 && pic_out->dts < 0)
+ {
+ w->config->h264.init_delay -= pic_out->dts;
+ }
+
+ switch (pic_out->sliceType)
+ {
+ case X265_TYPE_IDR:
+ buf->s.frametype = HB_FRAME_IDR;
+ break;
+ case X265_TYPE_I:
+ buf->s.frametype = HB_FRAME_I;
+ break;
+ case X265_TYPE_P:
+ buf->s.frametype = HB_FRAME_P;
+ break;
+ case X265_TYPE_B:
+ buf->s.frametype = HB_FRAME_B;
+ break;
+ case X265_TYPE_BREF:
+ buf->s.frametype = HB_FRAME_BREF;
+ break;
+ default:
+ buf->s.frametype = 0;
+ break;
+ }
+
+ if (pv->next_chapter_pts != AV_NOPTS_VALUE &&
+ pv->next_chapter_pts <= pic_out->pts &&
+ pic_out->sliceType == X265_TYPE_IDR)
+ {
+ // we're no longer looking for this chapter
+ pv->next_chapter_pts = AV_NOPTS_VALUE;
+
+ // get the chapter index from the list
+ struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0);
+ if (item != NULL)
+ {
+ // we're done with this chapter
+ hb_list_rem(pv->delayed_chapters, item);
+ free(item);
+
+ // we may still have another pending chapter
+ item = hb_list_item(pv->delayed_chapters, 0);
+ if (item != NULL)
+ {
+ // we're looking for this one now
+ // we still need it, don't remove it
+ pv->next_chapter_pts = item->start;
+ }
+ }
+ }
+
+ if (job->mux == HB_MUX_X265)
{
- fwrite((const char*)nal->payload, 1, nal->sizeBytes, pv->fout);
- nal++;
+ for (i = 0; i < nnal; i++)
+ {
+ fwrite(nal[i].payload, 1, nal[i].sizeBytes, pv->fout);
+ }
+ hb_buffer_close(&buf);
+ return NULL;
}
+
+ // discard empty buffers (no video)
+ if (buf->size <= 0)
+ {
+ hb_buffer_close(&buf);
+ }
+ return buf;
}
-static hb_buffer_t *x265_encode(hb_work_object_t *w, hb_buffer_t *in)
+static hb_buffer_t* x265_encode(hb_work_object_t *w, hb_buffer_t *in)
{
hb_work_private_t *pv = w->private_data;
hb_job_t *job = pv->job;
- x265_picture pic_out;
-
- pv->pic_in.stride[0] = in->plane[0].stride;
- pv->pic_in.stride[1] = in->plane[1].stride;
- pv->pic_in.stride[2] = in->plane[2].stride;
- pv->pic_in.planes[0] = in->plane[0].data;
- pv->pic_in.planes[1] = in->plane[1].data;
- pv->pic_in.planes[2] = in->plane[2].data;
- pv->pic_in.poc = pv->frames_in;
- pv->pic_in.pts = in->s.start;
- pv->pic_in.bitDepth = 8;
+ x265_picture pic_in, pic_out;
+ x265_nal *nal;
+ uint32_t nnal;
+
+ x265_picture_init(pv->param, &pic_in);
+
+ pic_in.stride[0] = in->plane[0].stride;
+ pic_in.stride[1] = in->plane[1].stride;
+ pic_in.stride[2] = in->plane[2].stride;
+ pic_in.planes[0] = in->plane[0].data;
+ pic_in.planes[1] = in->plane[1].data;
+ pic_in.planes[2] = in->plane[2].data;
+ pic_in.poc = pv->frames_in++;
+ pic_in.pts = in->s.start;
+ pic_in.bitDepth = 8;
if (in->s.new_chap && job->chapter_markers)
{
- /*
- * Chapters have to start with an IDR frame so request that this frame be
- * coded as IDR. Since there may be up to 16 frames currently buffered in
- * the encoder, remember the timestamp so when this frame finally pops out
- * of the encoder we'll mark its buffer as the start of a chapter.
- */
- pv->pic_in.sliceType = X265_TYPE_IDR;
if (pv->next_chapter_pts == AV_NOPTS_VALUE)
{
pv->next_chapter_pts = in->s.start;
@@ -300,10 +441,17 @@ static hb_buffer_t *x265_encode(hb_work_object_t *w, hb_buffer_t *in)
}
/* don't let 'work_loop' put a chapter mark on the wrong buffer */
in->s.new_chap = 0;
+ /*
+ * Chapters have to start with an IDR frame so request that this frame be
+ * coded as IDR. Since there may be up to 16 frames currently buffered in
+ * the encoder, remember the timestamp so when this frame finally pops out
+ * of the encoder we'll mark its buffer as the start of a chapter.
+ */
+ pic_in.sliceType = X265_TYPE_IDR;
}
else
{
- pv->pic_in.sliceType = X265_TYPE_AUTO;
+ pic_in.sliceType = X265_TYPE_AUTO;
}
if (pv->last_stop != in->s.start)
@@ -314,78 +462,62 @@ static hb_buffer_t *x265_encode(hb_work_object_t *w, hb_buffer_t *in)
pv->last_stop = in->s.stop;
save_frame_info(pv, in);
- x265_encoder_encode(pv->x265, &pv->p_nal, &pv->nal_count, &pv->pic_in, &pic_out);
- if (pv->nal_count > 0)
+ if (x265_encoder_encode(pv->x265, &nal, &nnal, &pic_in, &pic_out) > 0)
{
- writeNALs(pv, pv->p_nal, pv->nal_count);
- }
-
- if (pv->next_chapter_pts != AV_NOPTS_VALUE &&
- pv->next_chapter_pts <= pic_out.pts)
- {
- // we're no longer looking for this chapter
- pv->next_chapter_pts = AV_NOPTS_VALUE;
-
- // get the chapter index from the list
- struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0);
- if (item != NULL)
- {
- // we're done with this chapter
- hb_list_rem(pv->delayed_chapters, item);
- free(item);
-
- // we may still have another pending chapter
- item = hb_list_item(pv->delayed_chapters, 0);
- if (item != NULL)
- {
- // we're looking for this one now
- // we still need it, don't remove it
- pv->next_chapter_pts = item->start;
- }
- }
+ return nal_encode(w, &pic_out, nal, nnal);
}
-
return NULL;
}
int encx265Work(hb_work_object_t *w, hb_buffer_t **buf_in, hb_buffer_t **buf_out)
{
hb_work_private_t *pv = w->private_data;
- hb_buffer_t *in = *buf_in;
+ hb_buffer_t *in = *buf_in;
- *buf_out = NULL;
if (in->size <= 0)
{
- x265_picture pic_out;
- uint32_t i_nal;
+ uint32_t nnal;
x265_nal *nal;
+ x265_picture pic_out;
hb_buffer_t *last_buf = NULL;
- while (1)
+
+ // flush delayed frames
+ while (x265_encoder_encode(pv->x265, &nal, &nnal, NULL, &pic_out) > 0)
{
- x265_encoder_encode(pv->x265, &nal, &i_nal, NULL, &pic_out);
- if (i_nal <= 0)
- break;
- writeNALs(pv, nal, i_nal);
+ hb_buffer_t *buf = nal_encode(w, &pic_out, nal, nnal);
+ if (buf != NULL)
+ {
+ if (last_buf == NULL)
+ {
+ *buf_out = buf;
+ }
+ else
+ {
+ last_buf->next = buf;
+ }
+ last_buf = buf;
+ }
}
- // Flushed everything - add the eof to the end of the chain.
+
+ // add the EOF to the end of the chain
if (last_buf == NULL)
+ {
*buf_out = in;
+ }
else
+ {
last_buf->next = in;
-
+ }
*buf_in = NULL;
-
return HB_WORK_DONE;
}
- ++pv->frames_in;
- ++pv->frames_out;
*buf_out = x265_encode(w, in);
return HB_WORK_OK;
}
-const char * hb_x265_encopt_name(const char *name)
+const char* hb_x265_encopt_name(const char *name)
{
int i;
for (i = 0; hb_x265_encopt_synonyms[i][0] != NULL; i++)
diff --git a/libhb/internal.h b/libhb/internal.h
index a551333aa..fa3b36f5f 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -372,6 +372,12 @@ union hb_esconfig_u
struct
{
+ uint8_t headers[HB_CONFIG_MAX_SIZE];
+ int headers_length;
+ } h265;
+
+ struct
+ {
uint8_t headers[3][HB_CONFIG_MAX_SIZE];
} theora;
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index 7aa4c1415..3e9d9b9e9 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -307,6 +307,22 @@ static int avformatInit( hb_mux_object_t * m )
}
} break;
+ case HB_VCODEC_X265:
+ track->st->codec->codec_id = AV_CODEC_ID_HEVC;
+
+ if (job->config.h265.headers_length > 0)
+ {
+ priv_size = job->config.h265.headers_length;
+ priv_data = av_malloc(priv_size);
+ if (priv_data == NULL)
+ {
+ hb_error("malloc failure");
+ goto error;
+ }
+ memcpy(priv_data, job->config.h265.headers, priv_size);
+ }
+ break;
+
default:
hb_error("muxavformat: Unknown video codec: %x", job->vcodec);
goto error;